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..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 @@ -9,37 +9,65 @@ 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 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 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); } - public double T2Dfunction(int sector, int layer, int wire, double time){ + /** + * Converts a calibrated drift time into a distance-of-closest-approach (DOCA) for a + * 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}: + * {@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 timeToDistanceWireTable per-wire T2D calibration table + * @return the DOCA in mm + */ + 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); - // 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) - double p1 = (t2d.get(0) + t2d.get(1)*time); double p2 = (t2d.get(2) + t2d.get(3)*time); double p3 = (t2d.get(4) + t2d.get(5)*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 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 rawHitCutsTable, IndexedTable timeOffsetsTable, + IndexedTable timeToDistanceWireTable, IndexedTable timeOverThresholdTable, IndexedTable adcGainsTable) { ArrayList hits = new ArrayList<>(); if (!event.hasBank("AHDC::adc")) { @@ -92,16 +135,6 @@ 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 time = leadingEdgeTime - t0 - startTime; @@ -111,21 +144,30 @@ 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 + 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) && + (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, timeToDistanceWireTable); // ADC gain calibration double adcCal = adcRaw; @@ -144,7 +186,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 +214,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; } 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 8d4252fd28..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 @@ -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; @@ -57,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; @@ -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; } @@ -115,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; } } @@ -127,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 @@ -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); @@ -178,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 @@ -193,12 +198,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(); 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..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 @@ -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? @@ -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)); // 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); + // 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 = { + 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); 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