Skip to content
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since Cluster is an overloaded term. It would be a good idea to rename this class AHDCCluster. Or be explicit with interfaces using clusters

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.jlab.rec.ahdc.Cluster;
package org.jlab.rec.ahdc.AHDCCluster;

import org.jlab.rec.ahdc.Hit.Hit;
import org.jlab.rec.ahdc.PreCluster.PreCluster;
Expand All @@ -9,9 +9,9 @@
import org.jlab.geom.prim.Point3D;

/**
* Cluster are compose by 2 PreCluster on layer with a different stereo angle
* AHDCCluster are compose by 2 PreCluster on layer with a different stereo angle
*/
public class Cluster {
public class AHDCCluster {

private int _trackId = -1;
private double _Radius;
Expand Down Expand Up @@ -50,7 +50,7 @@ private static double stereoTwistFromLine(Line3D line) {

return wrapPi(phi1 - phi0);
}
public Cluster(PreCluster precluster, PreCluster other_precluster) {
public AHDCCluster(PreCluster precluster, PreCluster other_precluster) {
this._PreClusters_list = new ArrayList<>();
_PreClusters_list.add(precluster);
_PreClusters_list.add(other_precluster);
Expand Down Expand Up @@ -80,15 +80,44 @@ public Cluster(PreCluster precluster, PreCluster other_precluster) {
this._V = this._Y / (this._X * this._X + this._Y * this._Y);
}

public Cluster(double X, double Y, double Z) {
public AHDCCluster(double X, double Y, double Z) {
this._X = X;
this._Y = Y;
this._Z = Z;
}

/** Build an AHDCCluster from a single PreCluster (one layer of a superlayer).
* Used by the GNN path when a track covers a superlayer on only one
* stereo layer — no stereo pair is available, so Z is taken from the
* average wire-midpoint z of the PreCluster's hits rather than from a
* stereo-angle computation. DocaClusterRefiner falls back to a degenerate
* DocaCluster when {@code get_PreClusters_list().size() != 2}, so
* downstream is unaffected. */
public AHDCCluster(PreCluster precluster) {
this._PreClusters_list = new ArrayList<>();
_PreClusters_list.add(precluster);
this._Radius = precluster.get_Radius();
this._Phi = precluster.get_Phi();
this._X = precluster.get_X();
this._Y = precluster.get_Y();
this._Num_wire = (int) precluster.get_Num_wire();
double r2 = this._X * this._X + this._Y * this._Y;
if (r2 > 0.0) {
this._U = this._X / r2;
this._V = this._Y / r2;
}
double zSum = 0.0;
int zCount = 0;
for (Hit h : precluster.get_hits_list()) {
Line3D line = h.getLine();
if (line != null) { zSum += line.midpoint().z(); zCount++; }
}
this._Z = (zCount > 0) ? zSum / zCount : 0.0;
}

@Override
public String toString() {
return "Cluster{" + "_X=" + _X + ", _Y=" + _Y + ", _Z=" + _Z + '}';
return "AHDCCluster{" + "_X=" + _X + ", _Y=" + _Y + ", _Z=" + _Z + '}';
}

public ArrayList<PreCluster> get_PreClusters_list() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
package org.jlab.rec.ahdc.Cluster;
package org.jlab.rec.ahdc.AHDCCluster;

import org.jlab.rec.ahdc.PreCluster.PreCluster;

import java.util.ArrayList;
import java.util.List;

/** ClusterFinder
/** AHDCClusterFinder
*
* \todo description of what it does and how it works
*
*/
public class ClusterFinder {
public class AHDCClusterFinder {

private final ArrayList<Cluster> _AHDCClusters = new ArrayList<>();
private final ArrayList<Cluster> _list_with_maybe_same_cluster = new ArrayList<>();
private final ArrayList<AHDCCluster> _AHDCClusters = new ArrayList<>();
private final ArrayList<AHDCCluster> _list_with_maybe_same_cluster = new ArrayList<>();

public ClusterFinder() {}
public AHDCClusterFinder() {}

private void find_associate_cluster(PreCluster precluster, List<PreCluster> AHDC_precluster_list, int window, int minimal_distance, int super_layer, int layer, int associate_super_layer) {
//System.out.println(" precluster superlayer " + precluster.get_Super_layer() + " ref superlayer " + super_layer + " layer " + precluster.get_Layer() + " ref " + layer);
Expand Down Expand Up @@ -52,7 +52,7 @@ private void find_associate_cluster(PreCluster precluster, List<PreCluster> AHDC
if (best_precluster != null) {
precluster.set_Used(true);
best_precluster.set_Used(true);
Cluster new_Cluster = new Cluster(precluster, best_precluster);
AHDCCluster new_Cluster = new AHDCCluster(precluster, best_precluster);
_list_with_maybe_same_cluster.add(new_Cluster);
}
}
Expand Down Expand Up @@ -81,18 +81,18 @@ public void findCluster(List<PreCluster> AHDC_precluster_list) {
find_associate_cluster(precluster, AHDC_precluster_list, window, minimal_distance, 4, 2, 5);
}

for (Cluster cluster : _list_with_maybe_same_cluster) {
for (AHDCCluster cluster : _list_with_maybe_same_cluster) {
if (!containsCluster(_AHDCClusters, cluster.get_Phi(), cluster.get_Radius())) {
_AHDCClusters.add(cluster);
}
}
}

public boolean containsCluster(final List<Cluster> list, double phi, double radius) {
public boolean containsCluster(final List<AHDCCluster> list, double phi, double radius) {
return list.stream().anyMatch(o -> o.get_Radius() == (radius) && o.get_Phi() == phi);
}

public ArrayList<Cluster> get_AHDCClusters() {
public ArrayList<AHDCCluster> get_AHDCClusters() {
return _AHDCClusters;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.jlab.rec.ahdc.AI;

/** Normalization and graph-construction constants for the GNN track finder.
* Mirrors track-finding/gnn/config.py — keep in sync with the training config.
*/
final class GNNConstants {
private GNNConstants() {}

static final int NODE_FEAT_DIM = 10;
static final int EDGE_FEAT_DIM = 9;

// Model architecture parameters (control the minimum graph size at inference).
// GravNet progressive-k reaches 2*k, topk uses k+1 → N_nodes >= 2*k + 2.
// The exported model clamps topk(k+1) to N internally (see
// track-finding/export_torchscript.py::_knn_indices), so any graph with
// >=3 nodes runs without crashing. Smaller graphs can't form any edge
// with the MAX_LAYER_GAP rule anyway, so we skip them here.
static final int MIN_NODES = 3;

// Graph construction
static final int MAX_LAYER_GAP = 2;
static final double MAX_EDGE_DISTANCE = 35.0; // mm
static final double MAX_EDGE_DIST_SQ = MAX_EDGE_DISTANCE * MAX_EDGE_DISTANCE;

// Feature normalization
static final double MAX_R = 100.0; // mm
static final double DOCA_STD = 10.0; // mm
static final double Z_HALF_LENGTH = 200.0; // mm
static final double STEREO_ANGLE_MAX = 0.03; // rad
static final double STEREO_SCALE = 1.0 / STEREO_ANGLE_MAX;

// ATOF abs_layer convention from Python's build_graph
static final int ATOF_BAR_ABS_LAYER = 10; // component == 10
static final int ATOF_WEDGE_ABS_LAYER = 11; // all other components

// Track extraction: connected components at a single score threshold, matching
// gnn/evaluate.py (extract_tracks(..., method="cc", threshold=0.1)). Drop tracks
// with fewer than MIN_TRACK_NODES total nodes — same filter evaluate.py applies
// after the method call.
static final double TRACK_SCORE_THRESHOLD = 0.1;
static final int MIN_TRACK_NODES = 3;
}
Loading