Skip to content

Commit 34b31de

Browse files
Continue with RG-L Fix in the HitReader and AHDCEngine (#1207)
* 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. * 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. * 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. * ALERTEngine: fix IOOBE when Kalman loop skips a track row 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. * ALERTEngine: drop unreachable empty-hit guard, document invariant * AHDC: greedy non-overlap selection in AI track finding 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<Hit>). Sort predictions by score descending, greedily accept each one only if none of its PreClusters has already been claimed, enforcing one-hit one-track.
1 parent e7a1030 commit 34b31de

7 files changed

Lines changed: 197 additions & 118 deletions

File tree

reconstruction/alert/src/main/java/org/jlab/rec/ahdc/Hit/HitReader.java

Lines changed: 119 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,65 @@
99
import org.jlab.geom.detector.alert.AHDC.AlertDCDetector;
1010
import org.jlab.utils.groups.IndexedTable;
1111

12+
/**
13+
* Reads raw AHDC hits from the {@code AHDC::adc} bank, applies calibration corrections
14+
* (time offsets, time-over-threshold, ADC gains), filters them against per-wire cuts in
15+
* data mode, and builds the list of {@link Hit} objects used by downstream reconstruction.
16+
* In simulation mode, the per-wire cuts and data-only ADC/ToT corrections are bypassed,
17+
* and truth information is additionally read from the {@code MC::True} bank into {@link TrueHit}s.
18+
*/
1219
public class HitReader {
1320

1421
private ArrayList<Hit> _AHDCHits;
1522
private ArrayList<TrueHit> _TrueAHDCHits;
1623
private boolean sim = false;
1724

18-
private IndexedTable rawHitCutsTable;
19-
private IndexedTable timeOffsetsTable;
20-
private IndexedTable timeToDistanceWireTable;
21-
private IndexedTable timeOverThresholdTable;
22-
private IndexedTable adcGainsTable;
23-
24-
public HitReader(DataEvent event, AlertDCDetector detector, boolean simulation,
25-
IndexedTable rawHitCuts,
26-
IndexedTable timeOffsets,
27-
IndexedTable timeToDistanceWire,
28-
IndexedTable timeOverThreshold,
29-
IndexedTable adcGains) {
25+
/**
26+
* Constructs a HitReader and eagerly populates the hit lists from the given event.
27+
* After construction, retrieve the results via {@link #get_AHDCHits()} and
28+
* (in simulation) {@link #get_TrueAHDCHits()}.
29+
*
30+
* @param event current event containing the {@code AHDC::adc} bank (and {@code MC::True} in sim)
31+
* @param detector AHDC geometry used to resolve wire positions on each hit
32+
* @param simulation {@code true} for Monte Carlo events; disables data-only cuts and corrections
33+
* @param rawHitCutsTable per-wire acceptance cuts (time, ToT, ADC, pedestal min/max)
34+
* @param timeOffsetsTable per-wire {@code t0} offsets applied to the leading-edge time
35+
* @param timeToDistanceWireTable per-wire T2D calibration coefficients used to convert time to DOCA
36+
* @param timeOverThresholdTable per-wire ToT correction factors (applied in data mode only)
37+
* @param adcGainsTable per-wire ADC gain corrections (applied in data mode only)
38+
*/
39+
public HitReader(DataEvent event, AlertDCDetector detector, boolean simulation, IndexedTable rawHitCutsTable, IndexedTable timeOffsetsTable,
40+
IndexedTable timeToDistanceWireTable, IndexedTable timeOverThresholdTable, IndexedTable adcGainsTable) {
3041
sim = simulation;
31-
fetch_AHDCHits(event, detector, rawHitCuts, timeOffsets, timeToDistanceWire, timeOverThreshold, adcGains);
42+
fetch_AHDCHits(event, detector, rawHitCutsTable, timeOffsetsTable, timeToDistanceWireTable, timeOverThresholdTable, adcGainsTable);
3243
if (simulation) fetch_TrueAHDCHits(event);
3344
}
3445

35-
public double T2Dfunction(int sector, int layer, int wire, double time){
46+
/**
47+
* Converts a calibrated drift time into a distance-of-closest-approach (DOCA) for a
48+
* given wire, using the piecewise T2D calibration stored in {@code timeToDistanceWireTable}.
49+
*
50+
* <p>The result is a blend of three 1st-order polynomials {@code p1, p2, p3} stitched
51+
* together by two logistic transition functions {@code t1, t2}:
52+
* {@code doca = p1·(1-t1) + t1·p2·(1-t2) + t2·p3}. The coefficients are looked up
53+
* once per call via a hashed index on (sector, layer, wire).
54+
*
55+
* <p>Expected column order of the calibration row:
56+
* p1_int(0), p1_slope(1), p2_int(2), p2_slope(3), p3_int(4), p3_slope(5),
57+
* t1_x0(6), t1_width(7), t2_x0(8), t2_width(9), z0(10), z1(11), z2(12),
58+
* extra1(13), extra2(14), chi2ndf(15).
59+
*
60+
* @param sector AHDC sector index
61+
* @param layer packed layer index ({@code superlayer*10 + layer})
62+
* @param wire wire (component) id within the layer
63+
* @param time calibrated drift time in ns
64+
* @param timeToDistanceWireTable per-wire T2D calibration table
65+
* @return the DOCA in mm
66+
*/
67+
private double T2Dfunction(int sector, int layer, int wire, double time, IndexedTable timeToDistanceWireTable){
3668
long hash = timeToDistanceWireTable.getList().getIndexGenerator().hashCode(sector, layer, wire);
3769
List<Double> t2d = timeToDistanceWireTable.getDoublesByHash(hash);
3870

39-
// T2D function consists of three 1st order polynomials (p1, p2, p3) and two transition functions (t1, t2).
40-
// Column order: p1_int(0), p1_slope(1), p2_int(2), p2_slope(3), p3_int(4), p3_slope(5),
41-
// t1_x0(6), t1_width(7), t2_x0(8), t2_width(9), z0(10), z1(11), z2(12), extra1(13), extra2(14), chi2ndf(15)
42-
4371
double p1 = (t2d.get(0) + t2d.get(1)*time);
4472
double p2 = (t2d.get(2) + t2d.get(3)*time);
4573
double p3 = (t2d.get(4) + t2d.get(5)*time);
@@ -50,16 +78,31 @@ public double T2Dfunction(int sector, int layer, int wire, double time){
5078
return (p1)*(1.0 - t1) + (t1)*(p2)*(1.0 - t2) + (t2)*(p3);
5179
}
5280

53-
public final void fetch_AHDCHits(DataEvent event, AlertDCDetector detector,
54-
IndexedTable rawHitCuts, IndexedTable timeOffsets,
55-
IndexedTable timeToDistanceWire, IndexedTable totCorrTable,
56-
IndexedTable adcGains) {
57-
this.rawHitCutsTable = rawHitCuts;
58-
this.timeOffsetsTable = timeOffsets;
59-
this.timeToDistanceWireTable = timeToDistanceWire;
60-
this.timeOverThresholdTable = totCorrTable;
61-
this.adcGainsTable = adcGains;
62-
81+
/**
82+
* Reads the {@code AHDC::adc} bank, calibrates each raw row, and builds the list of
83+
* reconstructed {@link Hit}s. For each row the method:
84+
* <ol>
85+
* <li>applies the per-wire time offset {@code t0} (subtracting event start time in data mode),</li>
86+
* <li>in data mode, corrects time-over-threshold and enforces per-wire acceptance cuts
87+
* (time, ToT, ADC, pedestal, and {@code wfType <= 2}) — hits failing the cuts are dropped,</li>
88+
* <li>computes the DOCA from the calibrated time via {@link #T2Dfunction} (DOCA forced to 0
89+
* when {@code time < 0}),</li>
90+
* <li>in data mode, applies the per-wire ADC gain correction,</li>
91+
* <li>instantiates a {@link Hit}, resolves its wire position via the geometry, and stores the
92+
* calibrated ADC and ToT on it.</li>
93+
* </ol>
94+
* The resulting list is stored via {@link #set_AHDCHits(ArrayList)} (empty if the bank is absent).
95+
*
96+
* @param event current event
97+
* @param detector AHDC geometry used to set each hit's wire position
98+
* @param rawHitCutsTable per-wire acceptance cuts (data mode only)
99+
* @param timeOffsetsTable per-wire {@code t0}
100+
* @param timeToDistanceWireTable per-wire T2D coefficients
101+
* @param timeOverThresholdTable per-wire ToT correction factors (data mode only)
102+
* @param adcGainsTable per-wire ADC gain corrections (data mode only)
103+
*/
104+
private void fetch_AHDCHits(DataEvent event, AlertDCDetector detector, IndexedTable rawHitCutsTable, IndexedTable timeOffsetsTable,
105+
IndexedTable timeToDistanceWireTable, IndexedTable timeOverThresholdTable, IndexedTable adcGainsTable) {
63106
ArrayList<Hit> hits = new ArrayList<>();
64107

65108
if (!event.hasBank("AHDC::adc")) {
@@ -92,16 +135,6 @@ public final void fetch_AHDCHits(DataEvent event, AlertDCDetector detector,
92135
double adcOffset = bankDGTZ.getFloat("ped", i);
93136
int wfType = bankDGTZ.getShort("wfType", i);
94137

95-
// Raw hit cuts
96-
double t_min = rawHitCutsTable.getDoubleValue("t_min", sector, number, wire);
97-
double t_max = rawHitCutsTable.getDoubleValue("t_max", sector, number, wire);
98-
double tot_min = rawHitCutsTable.getDoubleValue("tot_min", sector, number, wire);
99-
double tot_max = rawHitCutsTable.getDoubleValue("tot_max", sector, number, wire);
100-
double adc_min = rawHitCutsTable.getDoubleValue("adc_min", sector, number, wire);
101-
double adc_max = rawHitCutsTable.getDoubleValue("adc_max", sector, number, wire);
102-
double ped_min = rawHitCutsTable.getDoubleValue("ped_min", sector, number, wire);
103-
double ped_max = rawHitCutsTable.getDoubleValue("ped_max", sector, number, wire);
104-
105138
// Time calibration
106139
double t0 = timeOffsetsTable.getDoubleValue("t0", sector, number, wire);
107140
double time = leadingEdgeTime - t0 - startTime;
@@ -111,21 +144,30 @@ public final void fetch_AHDCHits(DataEvent event, AlertDCDetector detector,
111144
if (!sim) {
112145
double totCorr = timeOverThresholdTable.getDoubleValue("totCorr", sector, number, wire);
113146
if (totCorr != 0.0) totUsed = timeOverThreshold * totCorr;
114-
}
115-
116-
// Hit selection (cuts)
117-
boolean passCuts =
118-
(wfType <= 2) &&
119-
(adcRaw >= adc_min) && (adcRaw <= adc_max) &&
120-
(time >= t_min) && (time <= t_max) &&
121-
(timeOverThreshold >= tot_min) && (timeOverThreshold <= tot_max) &&
122-
(adcOffset >= ped_min) && (adcOffset <= ped_max);
123147

124-
if (!passCuts && !sim) continue;
148+
// Hit selection (cuts) — only applied on data, bypassed in sim
149+
long hash = rawHitCutsTable.getList().getIndexGenerator().hashCode(sector, number, wire);
150+
double t_min = rawHitCutsTable.getDoubleValueByHash("t_min", hash);
151+
double t_max = rawHitCutsTable.getDoubleValueByHash("t_max", hash);
152+
double tot_min = rawHitCutsTable.getDoubleValueByHash("tot_min", hash);
153+
double tot_max = rawHitCutsTable.getDoubleValueByHash("tot_max", hash);
154+
double adc_min = rawHitCutsTable.getDoubleValueByHash("adc_min", hash);
155+
double adc_max = rawHitCutsTable.getDoubleValueByHash("adc_max", hash);
156+
double ped_min = rawHitCutsTable.getDoubleValueByHash("ped_min", hash);
157+
double ped_max = rawHitCutsTable.getDoubleValueByHash("ped_max", hash);
158+
159+
boolean passCuts =
160+
(wfType <= 2) &&
161+
(adcRaw >= adc_min) && (adcRaw <= adc_max) &&
162+
(time >= t_min) && (time <= t_max) &&
163+
(timeOverThreshold >= tot_min) && (timeOverThreshold <= tot_max) &&
164+
(adcOffset >= ped_min) && (adcOffset <= ped_max);
165+
166+
if (!passCuts) continue;
167+
}
125168

126169
// DOCA from calibrated time
127-
double doca = T2Dfunction(sector, number, wire, time);
128-
if (time < 0) doca = 0.0;
170+
double doca = (time < 0) ? 0.0 : T2Dfunction(sector, number, wire, time, timeToDistanceWireTable);
129171

130172
// ADC gain calibration
131173
double adcCal = adcRaw;
@@ -144,7 +186,15 @@ public final void fetch_AHDCHits(DataEvent event, AlertDCDetector detector,
144186
this.set_AHDCHits(hits);
145187
}
146188

147-
public final void fetch_TrueAHDCHits(DataEvent event) {
189+
/**
190+
* Reads Monte-Carlo truth information from the {@code MC::True} bank into a list of
191+
* {@link TrueHit}s (particle id and average hit position/energy). Called only when
192+
* the reader is constructed with {@code simulation = true}. If the bank is absent,
193+
* the resulting list is empty.
194+
*
195+
* @param event current event
196+
*/
197+
private void fetch_TrueAHDCHits(DataEvent event) {
148198

149199
ArrayList<TrueHit> truehits = new ArrayList<>();
150200

@@ -164,18 +214,36 @@ public final void fetch_TrueAHDCHits(DataEvent event) {
164214
this.set_TrueAHDCHits(truehits);
165215
}
166216

217+
/**
218+
* @return the calibrated AHDC hits produced from the current event; never {@code null}
219+
* (empty if the {@code AHDC::adc} bank is missing)
220+
*/
167221
public ArrayList<Hit> get_AHDCHits() {
168222
return _AHDCHits;
169223
}
170224

225+
/**
226+
* Replaces the internally stored list of AHDC hits. Primarily used by {@link #fetch_AHDCHits}.
227+
*
228+
* @param hits the list to store
229+
*/
171230
public void set_AHDCHits(ArrayList<Hit> hits) {
172231
this._AHDCHits = hits;
173232
}
174233

234+
/**
235+
* @return the MC-truth hits for the current event (populated only in simulation mode;
236+
* {@code null} for data events where {@code fetch_TrueAHDCHits} was not called)
237+
*/
175238
public ArrayList<TrueHit> get_TrueAHDCHits() {
176239
return _TrueAHDCHits;
177240
}
178241

242+
/**
243+
* Replaces the internally stored list of MC-truth hits. Primarily used by {@link #fetch_TrueAHDCHits}.
244+
*
245+
* @param trueHits the list to store
246+
*/
179247
public void set_TrueAHDCHits(ArrayList<TrueHit> trueHits) {
180248
this._TrueAHDCHits = trueHits;
181249
}

reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/ATOFHit.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class ATOFHit {
2727
private boolean isInACluster;
2828
private int associatedClusterIndex;
2929
int idTDC;
30-
private IndexedTable atofTimeOffsets;
30+
private IndexedTable atofTimeOffsetsTable;
3131

3232

3333
public int getSector() {
@@ -197,10 +197,10 @@ public final int convertTdcToTime() {
197197
if(this.startTime!= null) this.time -= this.startTime;
198198

199199
//Time offsets
200-
if (atofTimeOffsets == null) return 0;
200+
if (atofTimeOffsetsTable == null) return 0;
201201
int order0 = 0;
202-
double t0 = atofTimeOffsets.getDoubleValue("t0", this.sector, this.layer, this.component, order0);
203-
double tud = atofTimeOffsets.getDoubleValue("upstream_downstream", this.sector, this.layer, this.component, order0);
202+
double t0 = atofTimeOffsetsTable.getDoubleValue("t0", this.sector, this.layer, this.component, order0);
203+
double tud = atofTimeOffsetsTable.getDoubleValue("upstream_downstream", this.sector, this.layer, this.component, order0);
204204
//The rest of the constants are not used for now
205205
/*double twb = timeOffsets[2];
206206
double xtra1 = timeOffsets[3];
@@ -400,15 +400,15 @@ public double getPhi() {
400400
* spatial coordinates.
401401
*/
402402
public ATOFHit(int sector, int layer, int component, int order, int tdc, int tot, Float startTime, Detector atof,
403-
IndexedTable atofTimeOffsets) {
403+
IndexedTable atofTimeOffsetsTable) {
404404
this.sector = sector;
405405
this.layer = layer;
406406
this.component = component;
407407
this.order = order;
408408
this.tdc = tdc;
409409
this.tot = tot;
410410
this.startTime = startTime;
411-
this.atofTimeOffsets = atofTimeOffsets;
411+
this.atofTimeOffsetsTable = atofTimeOffsetsTable;
412412
this.isInACluster = false;
413413

414414
this.makeType();

reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/BarHit.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public final void computeEnergy() {
106106
this.setEnergy(Edep_up + Edep_down);
107107
}
108108

109-
public BarHit(ATOFHit hit_down, ATOFHit hit_up, IndexedTable atofEffectiveVelocity) {
109+
public BarHit(ATOFHit hit_down, ATOFHit hit_up, IndexedTable atofEffectiveVelocityTable) {
110110
boolean hits_match = hit_down.matchBar(hit_up);
111111
if (!hits_match) {
112112
throw new UnsupportedOperationException("Hits do not match \n");
@@ -122,7 +122,7 @@ public BarHit(ATOFHit hit_down, ATOFHit hit_up, IndexedTable atofEffectiveVeloci
122122
this.setY(hit_up.getY());
123123

124124
//CCDB readout for the effective velocity
125-
this.vEff = atofEffectiveVelocity.getDoubleValue("veff", this.getSector(), this.getLayer(), this.getComponent());
125+
this.vEff = atofEffectiveVelocityTable.getDoubleValue("veff", this.getSector(), this.getLayer(), this.getComponent());
126126
this.computeZ();
127127
this.computeTime();
128128
this.computeEnergy();

reconstruction/alert/src/main/java/org/jlab/rec/atof/hit/HitFinder.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ public void setWedgeHits(ArrayList<ATOFHit> wedge_hits) {
6565
* the sector/layer/component to x/y/z.
6666
*/
6767
public void findHits(DataEvent event, Detector atof, Float startTime,
68-
IndexedTable atofTimeOffsets,
69-
IndexedTable atofEffectiveVelocity) {
68+
IndexedTable atofTimeOffsetsTable,
69+
IndexedTable atofEffectiveVelocityTable) {
7070
//For each event a list of bar hits and a list of wedge hits are filled
7171
this.barHits.clear();
7272
this.wedgeHits.clear();
@@ -92,7 +92,7 @@ public void findHits(DataEvent event, Detector atof, Float startTime,
9292
int tot = bank.getInt("ToT", i);
9393

9494
//Building a Hit
95-
ATOFHit hit = new ATOFHit(sector, layer, component, order, tdc, tot, startTime, atof, atofTimeOffsets);
95+
ATOFHit hit = new ATOFHit(sector, layer, component, order, tdc, tot, startTime, atof, atofTimeOffsetsTable);
9696
if (hit.getEnergy() < 0.01) {
9797
continue; //energy threshold
9898
}
@@ -127,7 +127,7 @@ public void findHits(DataEvent event, Detector atof, Float startTime,
127127
//Matching the hits: if same module and different order, they make up a bar hit
128128
if (this_hit_up.matchBar(this_hit_down)) {
129129
//Bar hits are matched to ahdc tracks and listed
130-
BarHit this_bar_hit = new BarHit(this_hit_down, this_hit_up, atofEffectiveVelocity);
130+
BarHit this_bar_hit = new BarHit(this_hit_down, this_hit_up, atofEffectiveVelocityTable);
131131
//Only add bar hits for which the time sum is in time
132132
if(!this_bar_hit.isInTime()) continue;
133133
this.barHits.add(this_bar_hit);

0 commit comments

Comments
 (0)