From d36b62c3501a24a6945e745ea5a544e9ba3cd6ee Mon Sep 17 00:00:00 2001 From: MathieuOuillon Date: Fri, 10 Apr 2026 15:44:42 -0400 Subject: [PATCH 1/6] AHDC: skip raw-hit cut lookups in sim and tidy HitReader In simulation mode fetch_AHDCHits was still reading the per-wire rawHitCuts table even though the pass/fail result was discarded, which is wasted work and can fail when the sim CCDB run has no cut entries. The cut lookups and pass check are now nested inside the existing !sim branch alongside the ToT and ADC-gain corrections, so sim events go straight from time calibration to DOCA. While in the file: - Drop the rawHitCutsTable/timeOffsetsTable/... instance fields; the IndexedTables are passed through to fetch_AHDCHits and T2Dfunction as parameters, matching how they are used. - Make T2Dfunction / fetch_AHDCHits / fetch_TrueAHDCHits private; nothing outside HitReader calls them. - Collapse the DOCA branch to a single ternary. - Add Javadoc on the class, constructor, calibration pipeline, T2D function, and the hit/true-hit accessors. --- .../java/org/jlab/rec/ahdc/Hit/HitReader.java | 175 ++++++++++++------ 1 file changed, 121 insertions(+), 54 deletions(-) diff --git a/reconstruction/alert/src/main/java/org/jlab/rec/ahdc/Hit/HitReader.java b/reconstruction/alert/src/main/java/org/jlab/rec/ahdc/Hit/HitReader.java index e2953d7857..ac94c2a52a 100644 --- a/reconstruction/alert/src/main/java/org/jlab/rec/ahdc/Hit/HitReader.java +++ b/reconstruction/alert/src/main/java/org/jlab/rec/ahdc/Hit/HitReader.java @@ -9,36 +9,64 @@ import org.jlab.geom.detector.alert.AHDC.AlertDCDetector; import org.jlab.utils.groups.IndexedTable; +/** + * Reads raw AHDC hits from the {@code AHDC::adc} bank, applies calibration corrections + * (time offsets, time-over-threshold, ADC gains), filters them against per-wire cuts in + * data mode, and builds the list of {@link Hit} objects used by downstream reconstruction. + * In simulation mode, the per-wire cuts and data-only ADC/ToT corrections are bypassed, + * and truth information is additionally read from the {@code MC::True} bank into {@link TrueHit}s. + */ public class HitReader { private ArrayList _AHDCHits; private ArrayList _TrueAHDCHits; private boolean sim = false; - private IndexedTable rawHitCutsTable; - private IndexedTable timeOffsetsTable; - private IndexedTable timeToDistanceWireTable; - private IndexedTable timeOverThresholdTable; - private IndexedTable adcGainsTable; - - public HitReader(DataEvent event, AlertDCDetector detector, boolean simulation, - IndexedTable rawHitCuts, - IndexedTable timeOffsets, - IndexedTable timeToDistanceWire, - IndexedTable timeOverThreshold, - IndexedTable adcGains) { + /** + * Constructs a HitReader and eagerly populates the hit lists from the given event. + * After construction, retrieve the results via {@link #get_AHDCHits()} and + * (in simulation) {@link #get_TrueAHDCHits()}. + * + * @param event current event containing the {@code AHDC::adc} bank (and {@code MC::True} in sim) + * @param detector AHDC geometry used to resolve wire positions on each hit + * @param simulation {@code true} for Monte Carlo events; disables data-only cuts and corrections + * @param rawHitCuts per-wire acceptance cuts (time, ToT, ADC, pedestal min/max) + * @param timeOffsets per-wire {@code t0} offsets applied to the leading-edge time + * @param timeToDistanceWire per-wire T2D calibration coefficients used to convert time to DOCA + * @param timeOverThreshold per-wire ToT correction factors (applied in data mode only) + * @param adcGains per-wire ADC gain corrections (applied in data mode only) + */ + public HitReader(DataEvent event, AlertDCDetector detector, boolean simulation, IndexedTable rawHitCuts, IndexedTable timeOffsets, + IndexedTable timeToDistanceWire, IndexedTable timeOverThreshold, IndexedTable adcGains) { sim = simulation; fetch_AHDCHits(event, detector, rawHitCuts, timeOffsets, timeToDistanceWire, timeOverThreshold, adcGains); if (simulation) fetch_TrueAHDCHits(event); } - public double T2Dfunction(int sector, int layer, int wire, double time){ - long hash = timeToDistanceWireTable.getList().getIndexGenerator().hashCode(sector, layer, wire); - List t2d = timeToDistanceWireTable.getDoublesByHash(hash); - - // T2D function consists of three 1st order polynomials (p1, p2, p3) and two transition functions (t1, t2). - // Column order: p1_int(0), p1_slope(1), p2_int(2), p2_slope(3), p3_int(4), p3_slope(5), - // t1_x0(6), t1_width(7), t2_x0(8), t2_width(9), z0(10), z1(11), z2(12), extra1(13), extra2(14), chi2ndf(15) + /** + * Converts a calibrated drift time into a distance-of-closest-approach (DOCA) for a + * given wire, using the piecewise T2D calibration stored in {@code timeToDistanceWire}. + * + *

The result is a blend of three 1st-order polynomials {@code p1, p2, p3} stitched + * together by two logistic transition functions {@code t1, t2}: + * {@code doca = p1·(1-t1) + t1·p2·(1-t2) + t2·p3}. The coefficients are looked up + * once per call via a hashed index on (sector, layer, wire). + * + *

Expected column order of the calibration row: + * p1_int(0), p1_slope(1), p2_int(2), p2_slope(3), p3_int(4), p3_slope(5), + * t1_x0(6), t1_width(7), t2_x0(8), t2_width(9), z0(10), z1(11), z2(12), + * extra1(13), extra2(14), chi2ndf(15). + * + * @param sector AHDC sector index + * @param layer packed layer index ({@code superlayer*10 + layer}) + * @param wire wire (component) id within the layer + * @param time calibrated drift time in ns + * @param timeToDistanceWire per-wire T2D calibration table + * @return the DOCA in mm + */ + private double T2Dfunction(int sector, int layer, int wire, double time, IndexedTable timeToDistanceWire){ + long hash = timeToDistanceWire.getList().getIndexGenerator().hashCode(sector, layer, wire); + List t2d = timeToDistanceWire.getDoublesByHash(hash); double p1 = (t2d.get(0) + t2d.get(1)*time); double p2 = (t2d.get(2) + t2d.get(3)*time); @@ -50,16 +78,31 @@ public double T2Dfunction(int sector, int layer, int wire, double time){ return (p1)*(1.0 - t1) + (t1)*(p2)*(1.0 - t2) + (t2)*(p3); } - public final void fetch_AHDCHits(DataEvent event, AlertDCDetector detector, - IndexedTable rawHitCuts, IndexedTable timeOffsets, - IndexedTable timeToDistanceWire, IndexedTable totCorrTable, - IndexedTable adcGains) { - this.rawHitCutsTable = rawHitCuts; - this.timeOffsetsTable = timeOffsets; - this.timeToDistanceWireTable = timeToDistanceWire; - this.timeOverThresholdTable = totCorrTable; - this.adcGainsTable = adcGains; - + /** + * Reads the {@code AHDC::adc} bank, calibrates each raw row, and builds the list of + * reconstructed {@link Hit}s. For each row the method: + *

    + *
  1. applies the per-wire time offset {@code t0} (subtracting event start time in data mode),
  2. + *
  3. in data mode, corrects time-over-threshold and enforces per-wire acceptance cuts + * (time, ToT, ADC, pedestal, and {@code wfType <= 2}) — hits failing the cuts are dropped,
  4. + *
  5. computes the DOCA from the calibrated time via {@link #T2Dfunction} (DOCA forced to 0 + * when {@code time < 0}),
  6. + *
  7. in data mode, applies the per-wire ADC gain correction,
  8. + *
  9. instantiates a {@link Hit}, resolves its wire position via the geometry, and stores the + * calibrated ADC and ToT on it.
  10. + *
+ * The resulting list is stored via {@link #set_AHDCHits(ArrayList)} (empty if the bank is absent). + * + * @param event current event + * @param detector AHDC geometry used to set each hit's wire position + * @param rawHitCuts per-wire acceptance cuts (data mode only) + * @param timeOffsets per-wire {@code t0} + * @param timeToDistanceWire per-wire T2D coefficients + * @param timeOverThresholdTable per-wire ToT correction factors (data mode only) + * @param adcGains per-wire ADC gain corrections (data mode only) + */ + private void fetch_AHDCHits(DataEvent event, AlertDCDetector detector, IndexedTable rawHitCuts, IndexedTable timeOffsets, + IndexedTable timeToDistanceWire, IndexedTable timeOverThresholdTable, IndexedTable adcGains) { ArrayList hits = new ArrayList<>(); if (!event.hasBank("AHDC::adc")) { @@ -92,18 +135,8 @@ public final void fetch_AHDCHits(DataEvent event, AlertDCDetector detector, double adcOffset = bankDGTZ.getFloat("ped", i); int wfType = bankDGTZ.getShort("wfType", i); - // Raw hit cuts - double t_min = rawHitCutsTable.getDoubleValue("t_min", sector, number, wire); - double t_max = rawHitCutsTable.getDoubleValue("t_max", sector, number, wire); - double tot_min = rawHitCutsTable.getDoubleValue("tot_min", sector, number, wire); - double tot_max = rawHitCutsTable.getDoubleValue("tot_max", sector, number, wire); - double adc_min = rawHitCutsTable.getDoubleValue("adc_min", sector, number, wire); - double adc_max = rawHitCutsTable.getDoubleValue("adc_max", sector, number, wire); - double ped_min = rawHitCutsTable.getDoubleValue("ped_min", sector, number, wire); - double ped_max = rawHitCutsTable.getDoubleValue("ped_max", sector, number, wire); - // Time calibration - double t0 = timeOffsetsTable.getDoubleValue("t0", sector, number, wire); + double t0 = timeOffsets.getDoubleValue("t0", sector, number, wire); double time = leadingEdgeTime - t0 - startTime; // ToT correction @@ -111,26 +144,34 @@ public final void fetch_AHDCHits(DataEvent event, AlertDCDetector detector, if (!sim) { double totCorr = timeOverThresholdTable.getDoubleValue("totCorr", sector, number, wire); if (totCorr != 0.0) totUsed = timeOverThreshold * totCorr; - } - - // Hit selection (cuts) - boolean passCuts = - (wfType <= 2) && - (adcRaw >= adc_min) && (adcRaw <= adc_max) && - (time >= t_min) && (time <= t_max) && - (timeOverThreshold >= tot_min) && (timeOverThreshold <= tot_max) && - (adcOffset >= ped_min) && (adcOffset <= ped_max); - if (!passCuts && !sim) continue; + // Hit selection (cuts) — only applied on data, bypassed in sim + double t_min = rawHitCuts.getDoubleValue("t_min", sector, number, wire); + double t_max = rawHitCuts.getDoubleValue("t_max", sector, number, wire); + double tot_min = rawHitCuts.getDoubleValue("tot_min", sector, number, wire); + double tot_max = rawHitCuts.getDoubleValue("tot_max", sector, number, wire); + double adc_min = rawHitCuts.getDoubleValue("adc_min", sector, number, wire); + double adc_max = rawHitCuts.getDoubleValue("adc_max", sector, number, wire); + double ped_min = rawHitCuts.getDoubleValue("ped_min", sector, number, wire); + double ped_max = rawHitCuts.getDoubleValue("ped_max", sector, number, wire); + + boolean passCuts = + (wfType <= 2) && + (adcRaw >= adc_min) && (adcRaw <= adc_max) && + (time >= t_min) && (time <= t_max) && + (timeOverThreshold >= tot_min) && (timeOverThreshold <= tot_max) && + (adcOffset >= ped_min) && (adcOffset <= ped_max); + + if (!passCuts) continue; + } // DOCA from calibrated time - double doca = T2Dfunction(sector, number, wire, time); - if (time < 0) doca = 0.0; + double doca = (time < 0) ? 0.0 : T2Dfunction(sector, number, wire, time, timeToDistanceWire); // ADC gain calibration double adcCal = adcRaw; if (!sim) { - double gainCorr = adcGainsTable.getDoubleValue("gainCorr", sector, number, wire); + double gainCorr = adcGains.getDoubleValue("gainCorr", sector, number, wire); if (gainCorr != 0.0) adcCal = adcRaw * gainCorr; } @@ -144,7 +185,15 @@ public final void fetch_AHDCHits(DataEvent event, AlertDCDetector detector, this.set_AHDCHits(hits); } - public final void fetch_TrueAHDCHits(DataEvent event) { + /** + * Reads Monte-Carlo truth information from the {@code MC::True} bank into a list of + * {@link TrueHit}s (particle id and average hit position/energy). Called only when + * the reader is constructed with {@code simulation = true}. If the bank is absent, + * the resulting list is empty. + * + * @param event current event + */ + private void fetch_TrueAHDCHits(DataEvent event) { ArrayList truehits = new ArrayList<>(); @@ -164,18 +213,36 @@ public final void fetch_TrueAHDCHits(DataEvent event) { this.set_TrueAHDCHits(truehits); } + /** + * @return the calibrated AHDC hits produced from the current event; never {@code null} + * (empty if the {@code AHDC::adc} bank is missing) + */ public ArrayList get_AHDCHits() { return _AHDCHits; } + /** + * Replaces the internally stored list of AHDC hits. Primarily used by {@link #fetch_AHDCHits}. + * + * @param hits the list to store + */ public void set_AHDCHits(ArrayList hits) { this._AHDCHits = hits; } + /** + * @return the MC-truth hits for the current event (populated only in simulation mode; + * {@code null} for data events where {@code fetch_TrueAHDCHits} was not called) + */ public ArrayList get_TrueAHDCHits() { return _TrueAHDCHits; } + /** + * Replaces the internally stored list of MC-truth hits. Primarily used by {@link #fetch_TrueAHDCHits}. + * + * @param trueHits the list to store + */ public void set_TrueAHDCHits(ArrayList trueHits) { this._TrueAHDCHits = trueHits; } From 0a376e9a5b7075b8e5971d1c3ce938c90261d1e3 Mon Sep 17 00:00:00 2001 From: MathieuOuillon Date: Sat, 11 Apr 2026 19:18:19 -0400 Subject: [PATCH 2/6] AHDC: register missing output banks, fix track-finding mode leak, drop unused MaterialMap - Add AHDC::interclusters and AHDC::docaclusters to registerOutputBank so framework bank management (clearing, schema lookup) sees them. - Use a per-event effectiveMode local instead of overwriting the modeTrackFinding instance field when an event exceeds MAX_HITS_FOR_AI; previously a single noisy event forced CV_Distance for the rest of the run. - Remove the unused materialMap field and its MaterialMap/Material imports; the Kalman filter no longer consumes it from AHDCEngine. --- .../org/jlab/service/ahdc/AHDCEngine.java | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/reconstruction/alert/src/main/java/org/jlab/service/ahdc/AHDCEngine.java b/reconstruction/alert/src/main/java/org/jlab/service/ahdc/AHDCEngine.java index 8d4252fd28..02fb904c6e 100644 --- a/reconstruction/alert/src/main/java/org/jlab/service/ahdc/AHDCEngine.java +++ b/reconstruction/alert/src/main/java/org/jlab/service/ahdc/AHDCEngine.java @@ -1,7 +1,6 @@ package org.jlab.service.ahdc; import org.jlab.clas.reco.ReconstructionEngine; -import org.jlab.clas.tracking.kalmanfilter.Material; import org.jlab.io.base.DataBank; import org.jlab.io.base.DataEvent; import org.jlab.io.hipo.HipoDataSource; @@ -17,7 +16,6 @@ import org.jlab.rec.ahdc.Hit.Hit; import org.jlab.rec.ahdc.Hit.HitReader; import org.jlab.rec.ahdc.HoughTransform.HoughTransform; -import org.jlab.rec.ahdc.KalmanFilter.MaterialMap; import org.jlab.rec.ahdc.PreCluster.PreCluster; import org.jlab.rec.ahdc.PreCluster.PreClusterFinder; import org.jlab.rec.ahdc.Track.Track; @@ -43,10 +41,7 @@ public class AHDCEngine extends ReconstructionEngine { static final Logger LOGGER = Logger.getLogger(AHDCEngine.class.getName()); - private boolean simulation; - - /// Material Map used by Kalman filter - private HashMap materialMap; + private boolean simulation = false; private ModelTrackFinding modelTrackFinding; private ModeTrackFinding modeTrackFinding = ModeTrackFinding.AI_Track_Finding; @@ -76,9 +71,6 @@ public boolean init(ModeTrackFinding m) { public boolean init() { factory = (new AlertDCFactory()).createDetectorCLAS(new DatabaseConstantProvider()); - simulation = false; - - if (materialMap == null) materialMap = MaterialMap.generateMaterials(); String modeConfig = this.getEngineConfigString("Mode"); if (modeConfig != null) modeTrackFinding = ModeTrackFinding.valueOf(modeConfig); @@ -92,10 +84,8 @@ public boolean init() { tableMap.put("/calibration/alert/ahdc/time_over_threshold", 3); requireConstants(tableMap); - - this.getConstantsManager().setVariation("default"); - - this.registerOutputBank("AHDC::hits","AHDC::preclusters","AHDC::clusters","AHDC::track","AHDC::mc","AHDC::ai:prediction"); + this.getConstantsManager().setVariation("default"); + this.registerOutputBank("AHDC::hits","AHDC::preclusters","AHDC::clusters","AHDC::track","AHDC::mc","AHDC::ai:prediction","AHDC::interclusters","AHDC::docaclusters"); return true; } @@ -147,14 +137,15 @@ public boolean processDataEvent(DataEvent event) { // Otherwise, the conventional methods (Hough Transform or distance) use clusters. // Safety check: if too many hits, rely on conventional track finding + ModeTrackFinding effectiveMode = modeTrackFinding; if (AHDC_Hits.size() > MAX_HITS_FOR_AI) { LOGGER.info("Too many AHDC_Hits in AHDC::adc, rely on conventional track finding for this event"); - modeTrackFinding = ModeTrackFinding.CV_Distance; + effectiveMode = ModeTrackFinding.CV_Distance; } ArrayList AHDC_Tracks = new ArrayList<>(); - if (modeTrackFinding == ModeTrackFinding.AI_Track_Finding) { + if (effectiveMode == ModeTrackFinding.AI_Track_Finding) { // 1) Create inter-clusters from pre-clusters PreClustering preClustering = new PreClustering(); ArrayList inter_clusters = preClustering.mergePreclusters(AHDC_PreClusters); @@ -193,12 +184,12 @@ public boolean processDataEvent(DataEvent event) { ArrayList AHDC_Clusters = clusterfinder.get_AHDCClusters(); // 2) Find tracks using the selected conventional method - if (modeTrackFinding == ModeTrackFinding.CV_Distance) { + if (effectiveMode == ModeTrackFinding.CV_Distance) { Distance distance = new Distance(); distance.find_track(AHDC_Clusters); AHDC_Tracks = distance.get_AHDCTracks(); } - else if (modeTrackFinding == ModeTrackFinding.CV_Hough) { + else if (effectiveMode == ModeTrackFinding.CV_Hough) { HoughTransform houghtransform = new HoughTransform(); houghtransform.find_tracks(AHDC_Clusters); AHDC_Tracks = houghtransform.get_AHDCTracks(); From 66837807e1f8d94edc5e44a08a3cbb2d7b25548f Mon Sep 17 00:00:00 2001 From: MathieuOuillon Date: Sun, 12 Apr 2026 21:54:45 -0400 Subject: [PATCH 3/6] AHDC/ATOF/ALERT: suffix IndexedTable variables with "Table" Rename all IndexedTable fields, parameters, and javadoc references in the ALERT engine suite to carry a "Table" suffix, making calibration-table variables easy to spot at a glance. Touches AHDCEngine, ATOFEngine, ALERTEngine, HitReader, HitFinder, ATOFHit, and BarHit. --- .../java/org/jlab/rec/ahdc/Hit/HitReader.java | 81 ++++++++++--------- .../java/org/jlab/rec/atof/hit/ATOFHit.java | 12 +-- .../java/org/jlab/rec/atof/hit/BarHit.java | 4 +- .../java/org/jlab/rec/atof/hit/HitFinder.java | 8 +- .../org/jlab/service/ahdc/AHDCEngine.java | 24 +++--- .../org/jlab/service/alert/ALERTEngine.java | 4 +- .../org/jlab/service/atof/ATOFEngine.java | 18 ++--- 7 files changed, 76 insertions(+), 75 deletions(-) diff --git a/reconstruction/alert/src/main/java/org/jlab/rec/ahdc/Hit/HitReader.java b/reconstruction/alert/src/main/java/org/jlab/rec/ahdc/Hit/HitReader.java index ac94c2a52a..6268dd5dd2 100644 --- a/reconstruction/alert/src/main/java/org/jlab/rec/ahdc/Hit/HitReader.java +++ b/reconstruction/alert/src/main/java/org/jlab/rec/ahdc/Hit/HitReader.java @@ -27,25 +27,25 @@ public class HitReader { * After construction, retrieve the results via {@link #get_AHDCHits()} and * (in simulation) {@link #get_TrueAHDCHits()}. * - * @param event current event containing the {@code AHDC::adc} bank (and {@code MC::True} in sim) - * @param detector AHDC geometry used to resolve wire positions on each hit - * @param simulation {@code true} for Monte Carlo events; disables data-only cuts and corrections - * @param rawHitCuts per-wire acceptance cuts (time, ToT, ADC, pedestal min/max) - * @param timeOffsets per-wire {@code t0} offsets applied to the leading-edge time - * @param timeToDistanceWire per-wire T2D calibration coefficients used to convert time to DOCA - * @param timeOverThreshold per-wire ToT correction factors (applied in data mode only) - * @param adcGains per-wire ADC gain corrections (applied in data mode only) + * @param event current event containing the {@code AHDC::adc} bank (and {@code MC::True} in sim) + * @param detector AHDC geometry used to resolve wire positions on each hit + * @param simulation {@code true} for Monte Carlo events; disables data-only cuts and corrections + * @param rawHitCutsTable per-wire acceptance cuts (time, ToT, ADC, pedestal min/max) + * @param timeOffsetsTable per-wire {@code t0} offsets applied to the leading-edge time + * @param timeToDistanceWireTable per-wire T2D calibration coefficients used to convert time to DOCA + * @param timeOverThresholdTable per-wire ToT correction factors (applied in data mode only) + * @param adcGainsTable per-wire ADC gain corrections (applied in data mode only) */ - public HitReader(DataEvent event, AlertDCDetector detector, boolean simulation, IndexedTable rawHitCuts, IndexedTable timeOffsets, - IndexedTable timeToDistanceWire, IndexedTable timeOverThreshold, IndexedTable adcGains) { + public HitReader(DataEvent event, AlertDCDetector detector, boolean simulation, IndexedTable rawHitCutsTable, IndexedTable timeOffsetsTable, + IndexedTable timeToDistanceWireTable, IndexedTable timeOverThresholdTable, IndexedTable adcGainsTable) { sim = simulation; - fetch_AHDCHits(event, detector, rawHitCuts, timeOffsets, timeToDistanceWire, timeOverThreshold, adcGains); + fetch_AHDCHits(event, detector, rawHitCutsTable, timeOffsetsTable, timeToDistanceWireTable, timeOverThresholdTable, adcGainsTable); if (simulation) fetch_TrueAHDCHits(event); } /** * Converts a calibrated drift time into a distance-of-closest-approach (DOCA) for a - * given wire, using the piecewise T2D calibration stored in {@code timeToDistanceWire}. + * given wire, using the piecewise T2D calibration stored in {@code timeToDistanceWireTable}. * *

The result is a blend of three 1st-order polynomials {@code p1, p2, p3} stitched * together by two logistic transition functions {@code t1, t2}: @@ -57,16 +57,16 @@ public HitReader(DataEvent event, AlertDCDetector detector, boolean simulation, * t1_x0(6), t1_width(7), t2_x0(8), t2_width(9), z0(10), z1(11), z2(12), * extra1(13), extra2(14), chi2ndf(15). * - * @param sector AHDC sector index - * @param layer packed layer index ({@code superlayer*10 + layer}) - * @param wire wire (component) id within the layer - * @param time calibrated drift time in ns - * @param timeToDistanceWire per-wire T2D calibration table + * @param sector AHDC sector index + * @param layer packed layer index ({@code superlayer*10 + layer}) + * @param wire wire (component) id within the layer + * @param time calibrated drift time in ns + * @param timeToDistanceWireTable per-wire T2D calibration table * @return the DOCA in mm */ - private double T2Dfunction(int sector, int layer, int wire, double time, IndexedTable timeToDistanceWire){ - long hash = timeToDistanceWire.getList().getIndexGenerator().hashCode(sector, layer, wire); - List t2d = timeToDistanceWire.getDoublesByHash(hash); + private double T2Dfunction(int sector, int layer, int wire, double time, IndexedTable timeToDistanceWireTable){ + long hash = timeToDistanceWireTable.getList().getIndexGenerator().hashCode(sector, layer, wire); + List t2d = timeToDistanceWireTable.getDoublesByHash(hash); double p1 = (t2d.get(0) + t2d.get(1)*time); double p2 = (t2d.get(2) + t2d.get(3)*time); @@ -93,16 +93,16 @@ private double T2Dfunction(int sector, int layer, int wire, double time, Indexed * * The resulting list is stored via {@link #set_AHDCHits(ArrayList)} (empty if the bank is absent). * - * @param event current event - * @param detector AHDC geometry used to set each hit's wire position - * @param rawHitCuts per-wire acceptance cuts (data mode only) - * @param timeOffsets per-wire {@code t0} - * @param timeToDistanceWire per-wire T2D coefficients - * @param timeOverThresholdTable per-wire ToT correction factors (data mode only) - * @param adcGains per-wire ADC gain corrections (data mode only) + * @param event current event + * @param detector AHDC geometry used to set each hit's wire position + * @param rawHitCutsTable per-wire acceptance cuts (data mode only) + * @param timeOffsetsTable per-wire {@code t0} + * @param timeToDistanceWireTable per-wire T2D coefficients + * @param timeOverThresholdTable per-wire ToT correction factors (data mode only) + * @param adcGainsTable per-wire ADC gain corrections (data mode only) */ - private void fetch_AHDCHits(DataEvent event, AlertDCDetector detector, IndexedTable rawHitCuts, IndexedTable timeOffsets, - IndexedTable timeToDistanceWire, IndexedTable timeOverThresholdTable, IndexedTable adcGains) { + private void fetch_AHDCHits(DataEvent event, AlertDCDetector detector, IndexedTable rawHitCutsTable, IndexedTable timeOffsetsTable, + IndexedTable timeToDistanceWireTable, IndexedTable timeOverThresholdTable, IndexedTable adcGainsTable) { ArrayList hits = new ArrayList<>(); if (!event.hasBank("AHDC::adc")) { @@ -136,7 +136,7 @@ private void fetch_AHDCHits(DataEvent event, AlertDCDetector detector, IndexedTa int wfType = bankDGTZ.getShort("wfType", i); // Time calibration - double t0 = timeOffsets.getDoubleValue("t0", sector, number, wire); + double t0 = timeOffsetsTable.getDoubleValue("t0", sector, number, wire); double time = leadingEdgeTime - t0 - startTime; // ToT correction @@ -146,14 +146,15 @@ private void fetch_AHDCHits(DataEvent event, AlertDCDetector detector, IndexedTa if (totCorr != 0.0) totUsed = timeOverThreshold * totCorr; // Hit selection (cuts) — only applied on data, bypassed in sim - double t_min = rawHitCuts.getDoubleValue("t_min", sector, number, wire); - double t_max = rawHitCuts.getDoubleValue("t_max", sector, number, wire); - double tot_min = rawHitCuts.getDoubleValue("tot_min", sector, number, wire); - double tot_max = rawHitCuts.getDoubleValue("tot_max", sector, number, wire); - double adc_min = rawHitCuts.getDoubleValue("adc_min", sector, number, wire); - double adc_max = rawHitCuts.getDoubleValue("adc_max", sector, number, wire); - double ped_min = rawHitCuts.getDoubleValue("ped_min", sector, number, wire); - double ped_max = rawHitCuts.getDoubleValue("ped_max", sector, number, wire); + long hash = rawHitCutsTable.getList().getIndexGenerator().hashCode(sector, number, wire); + double t_min = rawHitCutsTable.getDoubleValueByHash("t_min", hash); + double t_max = rawHitCutsTable.getDoubleValueByHash("t_max", hash); + double tot_min = rawHitCutsTable.getDoubleValueByHash("tot_min", hash); + double tot_max = rawHitCutsTable.getDoubleValueByHash("tot_max", hash); + double adc_min = rawHitCutsTable.getDoubleValueByHash("adc_min", hash); + double adc_max = rawHitCutsTable.getDoubleValueByHash("adc_max", hash); + double ped_min = rawHitCutsTable.getDoubleValueByHash("ped_min", hash); + double ped_max = rawHitCutsTable.getDoubleValueByHash("ped_max", hash); boolean passCuts = (wfType <= 2) && @@ -166,12 +167,12 @@ private void fetch_AHDCHits(DataEvent event, AlertDCDetector detector, IndexedTa } // DOCA from calibrated time - double doca = (time < 0) ? 0.0 : T2Dfunction(sector, number, wire, time, timeToDistanceWire); + double doca = (time < 0) ? 0.0 : T2Dfunction(sector, number, wire, time, timeToDistanceWireTable); // ADC gain calibration double adcCal = adcRaw; if (!sim) { - double gainCorr = adcGains.getDoubleValue("gainCorr", sector, number, wire); + double gainCorr = adcGainsTable.getDoubleValue("gainCorr", sector, number, wire); if (gainCorr != 0.0) adcCal = adcRaw * gainCorr; } diff --git a/reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/ATOFHit.java b/reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/ATOFHit.java index c04a4ad4d4..a1812a237b 100644 --- a/reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/ATOFHit.java +++ b/reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/ATOFHit.java @@ -27,7 +27,7 @@ public class ATOFHit { private boolean isInACluster; private int associatedClusterIndex; int idTDC; - private IndexedTable atofTimeOffsets; + private IndexedTable atofTimeOffsetsTable; public int getSector() { @@ -197,10 +197,10 @@ public final int convertTdcToTime() { if(this.startTime!= null) this.time -= this.startTime; //Time offsets - if (atofTimeOffsets == null) return 0; + if (atofTimeOffsetsTable == null) return 0; int order0 = 0; - double t0 = atofTimeOffsets.getDoubleValue("t0", this.sector, this.layer, this.component, order0); - double tud = atofTimeOffsets.getDoubleValue("upstream_downstream", this.sector, this.layer, this.component, order0); + double t0 = atofTimeOffsetsTable.getDoubleValue("t0", this.sector, this.layer, this.component, order0); + double tud = atofTimeOffsetsTable.getDoubleValue("upstream_downstream", this.sector, this.layer, this.component, order0); //The rest of the constants are not used for now /*double twb = timeOffsets[2]; double xtra1 = timeOffsets[3]; @@ -400,7 +400,7 @@ public double getPhi() { * spatial coordinates. */ public ATOFHit(int sector, int layer, int component, int order, int tdc, int tot, Float startTime, Detector atof, - IndexedTable atofTimeOffsets) { + IndexedTable atofTimeOffsetsTable) { this.sector = sector; this.layer = layer; this.component = component; @@ -408,7 +408,7 @@ public ATOFHit(int sector, int layer, int component, int order, int tdc, int tot this.tdc = tdc; this.tot = tot; this.startTime = startTime; - this.atofTimeOffsets = atofTimeOffsets; + this.atofTimeOffsetsTable = atofTimeOffsetsTable; this.isInACluster = false; this.makeType(); diff --git a/reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/BarHit.java b/reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/BarHit.java index b60ecbdc2a..4dd8a94b12 100644 --- a/reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/BarHit.java +++ b/reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/BarHit.java @@ -106,7 +106,7 @@ public final void computeEnergy() { this.setEnergy(Edep_up + Edep_down); } - public BarHit(ATOFHit hit_down, ATOFHit hit_up, IndexedTable atofEffectiveVelocity) { + public BarHit(ATOFHit hit_down, ATOFHit hit_up, IndexedTable atofEffectiveVelocityTable) { boolean hits_match = hit_down.matchBar(hit_up); if (!hits_match) { throw new UnsupportedOperationException("Hits do not match \n"); @@ -122,7 +122,7 @@ public BarHit(ATOFHit hit_down, ATOFHit hit_up, IndexedTable atofEffectiveVeloci this.setY(hit_up.getY()); //CCDB readout for the effective velocity - this.vEff = atofEffectiveVelocity.getDoubleValue("veff", this.getSector(), this.getLayer(), this.getComponent()); + this.vEff = atofEffectiveVelocityTable.getDoubleValue("veff", this.getSector(), this.getLayer(), this.getComponent()); this.computeZ(); this.computeTime(); this.computeEnergy(); diff --git a/reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/HitFinder.java b/reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/HitFinder.java index 3970f11139..918cefa864 100644 --- a/reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/HitFinder.java +++ b/reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/HitFinder.java @@ -65,8 +65,8 @@ public void setWedgeHits(ArrayList wedge_hits) { * the sector/layer/component to x/y/z. */ public void findHits(DataEvent event, Detector atof, Float startTime, - IndexedTable atofTimeOffsets, - IndexedTable atofEffectiveVelocity) { + IndexedTable atofTimeOffsetsTable, + IndexedTable atofEffectiveVelocityTable) { //For each event a list of bar hits and a list of wedge hits are filled this.barHits.clear(); this.wedgeHits.clear(); @@ -92,7 +92,7 @@ public void findHits(DataEvent event, Detector atof, Float startTime, int tot = bank.getInt("ToT", i); //Building a Hit - ATOFHit hit = new ATOFHit(sector, layer, component, order, tdc, tot, startTime, atof, atofTimeOffsets); + ATOFHit hit = new ATOFHit(sector, layer, component, order, tdc, tot, startTime, atof, atofTimeOffsetsTable); if (hit.getEnergy() < 0.01) { continue; //energy threshold } @@ -127,7 +127,7 @@ public void findHits(DataEvent event, Detector atof, Float startTime, //Matching the hits: if same module and different order, they make up a bar hit if (this_hit_up.matchBar(this_hit_down)) { //Bar hits are matched to ahdc tracks and listed - BarHit this_bar_hit = new BarHit(this_hit_down, this_hit_up, atofEffectiveVelocity); + BarHit this_bar_hit = new BarHit(this_hit_down, this_hit_up, atofEffectiveVelocityTable); //Only add bar hits for which the time sum is in time if(!this_bar_hit.isInTime()) continue; this.barHits.add(this_bar_hit); diff --git a/reconstruction/alert/src/main/java/org/jlab/service/ahdc/AHDCEngine.java b/reconstruction/alert/src/main/java/org/jlab/service/ahdc/AHDCEngine.java index 02fb904c6e..566d422317 100644 --- a/reconstruction/alert/src/main/java/org/jlab/service/ahdc/AHDCEngine.java +++ b/reconstruction/alert/src/main/java/org/jlab/service/ahdc/AHDCEngine.java @@ -52,11 +52,11 @@ public class AHDCEngine extends ReconstructionEngine { private ModeAHDC ahdcExtractor = new ModeAHDC(); // AHDC calibration tables (instance-level, refreshed on run change) - private IndexedTable ahdcTimeOffsets; - private IndexedTable ahdcTimeToDistanceWire; - private IndexedTable ahdcRawHitCuts; - private IndexedTable ahdcAdcGains; - private IndexedTable ahdcTimeOverThreshold; + private IndexedTable ahdcTimeOffsetsTable; + private IndexedTable ahdcTimeToDistanceWireTable; + private IndexedTable ahdcRawHitCutsTable; + private IndexedTable ahdcAdcGainsTable; + private IndexedTable ahdcTimeOverThresholdTable; int Run = -1; @@ -105,11 +105,11 @@ public boolean processDataEvent(DataEvent event) { return false; } if(Run != newRun) { - ahdcTimeOffsets = this.getConstantsManager().getConstants(newRun, "/calibration/alert/ahdc/time_offsets"); - ahdcTimeToDistanceWire = this.getConstantsManager().getConstants(newRun, "/calibration/alert/ahdc/time_to_distance_wire"); - ahdcRawHitCuts = this.getConstantsManager().getConstants(newRun, "/calibration/alert/ahdc/raw_hit_cuts"); - ahdcAdcGains = this.getConstantsManager().getConstants(newRun, "/calibration/alert/ahdc/gains"); - ahdcTimeOverThreshold = this.getConstantsManager().getConstants(newRun, "/calibration/alert/ahdc/time_over_threshold"); + ahdcTimeOffsetsTable = this.getConstantsManager().getConstants(newRun, "/calibration/alert/ahdc/time_offsets"); + ahdcTimeToDistanceWireTable = this.getConstantsManager().getConstants(newRun, "/calibration/alert/ahdc/time_to_distance_wire"); + ahdcRawHitCutsTable = this.getConstantsManager().getConstants(newRun, "/calibration/alert/ahdc/raw_hit_cuts"); + ahdcAdcGainsTable = this.getConstantsManager().getConstants(newRun, "/calibration/alert/ahdc/gains"); + ahdcTimeOverThresholdTable = this.getConstantsManager().getConstants(newRun, "/calibration/alert/ahdc/time_over_threshold"); Run = newRun; } } @@ -117,8 +117,8 @@ public boolean processDataEvent(DataEvent event) { if (event.hasBank("AHDC::adc")) { // I) Read raw hits HitReader hitReader = new HitReader(event, factory, simulation, - ahdcRawHitCuts, ahdcTimeOffsets, ahdcTimeToDistanceWire, - ahdcTimeOverThreshold, ahdcAdcGains); + ahdcRawHitCutsTable, ahdcTimeOffsetsTable, ahdcTimeToDistanceWireTable, + ahdcTimeOverThresholdTable, ahdcAdcGainsTable); ArrayList AHDC_Hits = hitReader.get_AHDCHits(); // II) Create PreClusters diff --git a/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java b/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java index 3b6444e46e..c2de5aa18d 100644 --- a/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java +++ b/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java @@ -76,7 +76,7 @@ public class ALERTEngine extends ReconstructionEngine { private ModelPrePID modelPrePID; // AHDC calibration table (refreshed on run change) - private IndexedTable ahdcAdcGains; + private IndexedTable ahdcAdcGainsTable; public void setB(double B) { this.b = B; @@ -152,7 +152,7 @@ public boolean processDataEvent(DataEvent event) { if (run.get() == 0 || (run.get() != 0 && run.get() != newRun)) { run.set(newRun); - ahdcAdcGains = this.getConstantsManager().getConstants(newRun, "/calibration/alert/ahdc/gains"); + ahdcAdcGainsTable = this.getConstantsManager().getConstants(newRun, "/calibration/alert/ahdc/gains"); } //Do we need to read the event vx,vy,vz? diff --git a/reconstruction/alert/src/main/java/org/jlab/service/atof/ATOFEngine.java b/reconstruction/alert/src/main/java/org/jlab/service/atof/ATOFEngine.java index 7e4974ef3a..ad15498413 100644 --- a/reconstruction/alert/src/main/java/org/jlab/service/atof/ATOFEngine.java +++ b/reconstruction/alert/src/main/java/org/jlab/service/atof/ATOFEngine.java @@ -55,10 +55,10 @@ public Detector getATOF() { int Run = -1; // ATOF calibration tables (instance-level, refreshed on run change) - private IndexedTable atofEffectiveVelocity; - private IndexedTable atofTimeWalk; - private IndexedTable atofAttenuationLength; - private IndexedTable atofTimeOffsets; + private IndexedTable atofEffectiveVelocityTable; + private IndexedTable atofTimeWalkTable; + private IndexedTable atofAttenuationLengthTable; + private IndexedTable atofTimeOffsetsTable; @Override public boolean processDataEvent(DataEvent event) { @@ -87,10 +87,10 @@ public boolean processDataEvent(DataEvent event) { } int newRun = runNo; if(Run!=newRun) { - atofEffectiveVelocity = this.getConstantsManager().getConstants(newRun, "/calibration/alert/atof/effective_velocity"); - atofTimeWalk = this.getConstantsManager().getConstants(newRun, "/calibration/alert/atof/time_walk"); - atofAttenuationLength = this.getConstantsManager().getConstants(newRun, "/calibration/alert/atof/attenuation"); - atofTimeOffsets = this.getConstantsManager().getConstants(newRun, "/calibration/alert/atof/time_offsets"); + atofEffectiveVelocityTable = this.getConstantsManager().getConstants(newRun, "/calibration/alert/atof/effective_velocity"); + atofTimeWalkTable = this.getConstantsManager().getConstants(newRun, "/calibration/alert/atof/time_walk"); + atofAttenuationLengthTable = this.getConstantsManager().getConstants(newRun, "/calibration/alert/atof/attenuation"); + atofTimeOffsetsTable = this.getConstantsManager().getConstants(newRun, "/calibration/alert/atof/time_offsets"); Run = newRun; } @@ -111,7 +111,7 @@ public boolean processDataEvent(DataEvent event) { //Hit finder init HitFinder hitfinder = new HitFinder(); - hitfinder.findHits(event, ATOF, startTime, atofTimeOffsets, atofEffectiveVelocity); + hitfinder.findHits(event, ATOF, startTime, atofTimeOffsetsTable, atofEffectiveVelocityTable); ArrayList WedgeHits = hitfinder.getWedgeHits(); ArrayList BarHits = hitfinder.getBarHits(); //Exit if hit lists are empty From cb1eaac1457368598e0f08895409b2f73cf431b3 Mon Sep 17 00:00:00 2001 From: MathieuOuillon Date: Mon, 13 Apr 2026 15:14:50 -0400 Subject: [PATCH 4/6] ALERTEngine: fix IOOBE when Kalman loop skips a track row MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Kalman preprocessing loop read tracks back via AHDC_tracks.get(row), which breaks as soon as the empty-hit guard skips a row and desynchronises row from the list index. Build each Track through a local reference, initialise position/momentum/trackid, then append — so skipped rows never poison later iterations. Also log a warning on the skip branch so the upstream "AHDC::track row with no matching AHDC::hits" case is visible. --- .../org/jlab/service/alert/ALERTEngine.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java b/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java index c2de5aa18d..322b865ea3 100644 --- a/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java +++ b/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java @@ -359,20 +359,26 @@ public boolean processDataEvent(DataEvent event) { AHDC_hits.add(hit); } } - if (AHDC_hits.isEmpty()) continue; // It can happen that a track has no associated hit, in this case we skip it for the Kalman Filter - AHDC_tracks.add(new Track(AHDC_hits)); + if (AHDC_hits.isEmpty()) { + LOGGER.warning("ALERTEngine: AHDC::track row " + row + + " (trackid=" + trackid + ") has no matching hits in AHDC::hits, skipping"); + continue; + } // Initialise the position and the momentum using the information of the AHDC::track // position : mm // momentum : MeV - double x = trackBank.getFloat("x", row); - double y = trackBank.getFloat("y", row); - double z = trackBank.getFloat("z", row); - double px = trackBank.getFloat("px", row); - double py = trackBank.getFloat("py", row); - double pz = trackBank.getFloat("pz", row); - double[] vec = {x, y, z, px, py, pz}; - AHDC_tracks.get(row).setPositionAndMomentumVec(vec); - AHDC_tracks.get(row).set_trackId(trackid); + Track newTrack = new Track(AHDC_hits); + double[] vec = { + trackBank.getFloat("x", row), + trackBank.getFloat("y", row), + trackBank.getFloat("z", row), + trackBank.getFloat("px", row), + trackBank.getFloat("py", row), + trackBank.getFloat("pz", row) + }; + newTrack.setPositionAndMomentumVec(vec); + newTrack.set_trackId(trackid); + AHDC_tracks.add(newTrack); } // intialise the Kalman Filter double magfieldfactor = runBank.getFloat("solenoid", 0); From fe456f044c3b7aca7a52f61b128a1571f4eb59e8 Mon Sep 17 00:00:00 2001 From: MathieuOuillon Date: Mon, 13 Apr 2026 15:46:18 -0400 Subject: [PATCH 5/6] ALERTEngine: drop unreachable empty-hit guard, document invariant --- .../main/java/org/jlab/service/alert/ALERTEngine.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java b/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java index 322b865ea3..2f5794da01 100644 --- a/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java +++ b/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java @@ -359,14 +359,14 @@ public boolean processDataEvent(DataEvent event) { AHDC_hits.add(hit); } } - if (AHDC_hits.isEmpty()) { - LOGGER.warning("ALERTEngine: AHDC::track row " + row - + " (trackid=" + trackid + ") has no matching hits in AHDC::hits, skipping"); - continue; - } // Initialise the position and the momentum using the information of the AHDC::track // position : mm // momentum : MeV + // Invariant: AHDC_hits is non-empty. AHDCEngine's AI_Track_Finding path only writes + // tracks whose clusters hold >= 6 precluster hits (see AHDCEngine:206 filter and + // TrackPrediction cluster pairing), and those same Hit instances are the ones + // serialised to AHDC::hits. If this ever flips, the get(0) inside + // Track(ArrayList) fails loudly here, which is the right signal. Track newTrack = new Track(AHDC_hits); double[] vec = { trackBank.getFloat("x", row), From 38f326599a9ca83a7ef654c0c95ad9484c3a7ee6 Mon Sep 17 00:00:00 2001 From: MathieuOuillon Date: Mon, 13 Apr 2026 16:15:58 -0400 Subject: [PATCH 6/6] AHDC: greedy non-overlap selection in AI track finding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AI candidate generator routinely emits overlapping TrackPredictions that share PreCluster (and therefore Hit) references. Accepting all predictions above threshold let later tracks silently steal earlier tracks' hits via in-place set_trackId() mutation, leaving orphan rows in AHDC::track with no matching rows in AHDC::hits — which in turn crashed the ALERTEngine Kalman loop with IndexOutOfBoundsException inside Track(ArrayList). Sort predictions by score descending, greedily accept each one only if none of its PreClusters has already been claimed, enforcing one-hit one-track. --- .../org/jlab/service/ahdc/AHDCEngine.java | 22 +++++++++++++++---- .../org/jlab/service/alert/ALERTEngine.java | 8 +++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/reconstruction/alert/src/main/java/org/jlab/service/ahdc/AHDCEngine.java b/reconstruction/alert/src/main/java/org/jlab/service/ahdc/AHDCEngine.java index 566d422317..66b965825c 100644 --- a/reconstruction/alert/src/main/java/org/jlab/service/ahdc/AHDCEngine.java +++ b/reconstruction/alert/src/main/java/org/jlab/service/ahdc/AHDCEngine.java @@ -169,12 +169,26 @@ public boolean processDataEvent(DataEvent event) { throw new RuntimeException(e); } - // 4) Use the output for the AI model to select the good tracks among the candidates + // 4) Select good tracks via greedy non-overlap: sort predictions by score + // descending, accept the highest-scoring prediction, mark its PreClusters + // as claimed, and skip any later prediction that reuses a claimed PreCluster. + // The AI candidate generator routinely emits overlapping predictions (each + // PreCluster can feed several combinations), and because set_trackId mutates + // the shared Hit references in place, a naive "accept all above threshold" + // pass would let later tracks silently steal earlier tracks' hits and leave + // them orphaned in AHDC::hits. Greedy selection enforces one-hit-one-track. + predictions.sort((a, b) -> Float.compare(b.getPrediction(), a.getPrediction())); + Set claimedPreclusters = new HashSet<>(); for (TrackPrediction t : predictions) { - if (t.getPrediction() > TRACK_FINDING_AI_THRESHOLD) AHDC_Tracks.add(new Track(t.getClusters())); + if (t.getPrediction() <= TRACK_FINDING_AI_THRESHOLD) continue; + boolean overlaps = false; + for (PreCluster pc : t.getPreclusters()) { + if (claimedPreclusters.contains(pc)) { overlaps = true; break; } + } + if (overlaps) continue; + claimedPreclusters.addAll(t.getPreclusters()); + AHDC_Tracks.add(new Track(t.getClusters())); } - // The assignment of Track ID to all objects is done in the Kalman filter step below - // I don't know if it is a good idea. } else { // Conventional Track Finding: Hough Transform or Distance: use cluster informations to find tracks diff --git a/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java b/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java index 2f5794da01..a899e20c8a 100644 --- a/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java +++ b/reconstruction/alert/src/main/java/org/jlab/service/alert/ALERTEngine.java @@ -362,10 +362,10 @@ public boolean processDataEvent(DataEvent event) { // Initialise the position and the momentum using the information of the AHDC::track // position : mm // momentum : MeV - // Invariant: AHDC_hits is non-empty. AHDCEngine's AI_Track_Finding path only writes - // tracks whose clusters hold >= 6 precluster hits (see AHDCEngine:206 filter and - // TrackPrediction cluster pairing), and those same Hit instances are the ones - // serialised to AHDC::hits. If this ever flips, the get(0) inside + // Invariant: AHDC_hits is non-empty. AHDCEngine's AI_Track_Finding path uses greedy + // non-overlap selection so each PreCluster (and thus each Hit) belongs to at most one + // surviving track, so the set_trackId stamping is unambiguous and every AHDC::track + // row has matching AHDC::hits rows. If this invariant ever flips, the get(0) inside // Track(ArrayList) fails loudly here, which is the right signal. Track newTrack = new Track(AHDC_hits); double[] vec = {