4from BaseDetector
import BaseDetector
8 def __init__(self, name, intree, outtree=None) -> None:
10 super().
__init__(name, intree,
"Splitcal", outtree=outtree)
13 self.
reco = ROOT.std.vector(
"splitcalCluster")()
15 tree_for_output = self.
outtree if outtree
is not None else intree
24 """Fill detector hit branches.
26 Note: This method is now a no-op to prevent double-filling.
27 All branches are filled synchronously by recoTree.Fill()
in the main loop.
32 """Digitize splitcal hits and perform cluster reconstruction."""
35 for point
in self.
intree.splitcalPoint:
36 detector_id = point.GetDetectorID()
37 if detector_id
not in points_by_detID:
38 points_by_detID[detector_id] = []
39 points_by_detID[detector_id].append(point)
42 for detector_id, points
in points_by_detID.items():
44 point_vector = ROOT.std.vector(
"splitcalPoint")()
46 point_vector.push_back(point)
48 hit = ROOT.splitcalHit(point_vector, self.
intree.t0)
49 self.
det.push_back(hit)
55 """Perform cluster reconstruction from digitized hits."""
57 noise_energy_threshold = 0.002
58 list_hits_above_threshold = []
60 for idx, hit
in enumerate(self.
det):
61 if hit.GetEnergy() > noise_energy_threshold:
63 list_hits_above_threshold.append(hit)
64 hit_to_index[id(hit)] = idx
66 if not list_hits_above_threshold:
76 list_final_clusters = {}
77 index_final_cluster = 0
79 for i
in list_clusters_of_hits:
82 for hit
in list_clusters_of_hits[i]:
85 list_hits_x.append(hit)
87 list_hits_y.append(hit)
98 weights_from_x_splitting = {}
99 for index_subcluster
in list_of_subclusters_x:
101 weight = subcluster_energy_x / cluster_energy_x
if cluster_energy_x > 0
else 0
102 weights_from_x_splitting[index_subcluster] = weight
113 weights_from_y_splitting = {}
114 for index_subcluster
in list_of_subclusters_y:
116 weight = subcluster_energy_y / cluster_energy_y
if cluster_energy_y > 0
else 0
117 weights_from_y_splitting[index_subcluster] = weight
120 if len(list_of_subclusters_x) == 1
and len(list_of_subclusters_y) == 1:
121 list_final_clusters[index_final_cluster] = [(hit, 1.0)
for hit
in list_clusters_of_hits[i]]
122 index_final_cluster += 1
124 for ix
in list_of_subclusters_x:
125 for iy
in list_of_subclusters_y:
128 for hit
in list_of_subclusters_y[iy]:
129 hit_weight_list.append((hit, weights_from_x_splitting[ix]))
130 for hit
in list_of_subclusters_x[ix]:
131 hit_weight_list.append((hit, weights_from_y_splitting[iy]))
133 list_final_clusters[index_final_cluster] = hit_weight_list
134 index_final_cluster += 1
137 for i
in list_final_clusters:
139 cluster = ROOT.splitcalCluster()
142 for hit, weight
in list_final_clusters[i]:
143 hit_index = hit_to_index[id(hit)]
144 cluster.AddHit(hit_index, weight)
146 cluster.SetIndex(int(i))
147 cluster.ComputeEtaPhiE(self.
det)
148 self.
reco.push_back(cluster)
151 """Merge fragments into the closest subcluster."""
152 list_subclusters_excluding_fragments = {}
153 fragment_indices = []
154 subclusters_indices = []
158 if subcluster_size < 5:
159 fragment_indices.append(k)
161 subclusters_indices.append(k)
164 if len(subclusters_indices) == 0
and len(fragment_indices) != 0:
165 subclusters_indices.append(0)
168 for index_fragment
in fragment_indices:
173 for index_subcluster
in subclusters_indices:
175 if first_hit_fragment.IsX():
176 distance = fabs(first_hit_fragment.GetX() - first_hit_subcluster.GetX())
178 distance = fabs(first_hit_fragment.GetY() - first_hit_subcluster.GetY())
180 if minDistance < 0
or distance < minDistance:
181 minDistance = distance
182 minIndex = index_subcluster
184 if minIndex != index_fragment:
187 for counter, index_subcluster
in enumerate(subclusters_indices):
190 return list_subclusters_excluding_fragments
193 """Calculate total energy of hits in a cluster."""
195 for hit
in list_hits:
196 energy += hit.GetEnergy()
200 """Perform clustering algorithm on input hits."""
201 list_hits_in_cluster = {}
205 if hit.IsUsed() == 1:
210 if len(neighbours) < 1:
213 cluster_index = cluster_index + 1
215 list_hits_in_cluster[cluster_index] = []
216 list_hits_in_cluster[cluster_index].append(hit)
218 for neighbouringHit
in neighbours:
219 if neighbouringHit.IsUsed() == 1:
222 neighbouringHit.SetIsUsed(1)
223 list_hits_in_cluster[cluster_index].append(neighbouringHit)
227 if len(expand_neighbours) >= 1:
228 for additionalHit
in expand_neighbours:
229 if additionalHit
not in neighbours:
230 neighbours.append(additionalHit)
232 return list_hits_in_cluster
235 """Find neighbouring hits for clustering."""
237 err_x_1 = hit.GetXError()
238 err_y_1 = hit.GetYError()
239 err_z_1 = hit.GetZError()
244 err_x_1 = err_x_1 * max_gap
246 err_y_1 = err_y_1 * max_gap
250 Dx = fabs(hit2.GetX() - hit.GetX())
251 Dy = fabs(hit2.GetY() - hit.GetY())
252 Dz = fabs(hit2.GetZ() - hit.GetZ())
253 err_x_2 = hit2.GetXError()
254 err_y_2 = hit2.GetYError()
255 err_z_2 = hit2.GetZError()
258 err_x_2 = err_x_2 * max_gap
260 err_y_2 = err_y_2 * max_gap
264 Dx <= (err_x_1 + err_x_2)
265 and Dz <= 2 * (err_z_1 + err_z_2)
266 and ((Dy <= (err_y_1 + err_y_2)
and Dz > 0.0)
or (Dy == 0))
268 list_neighbours.append(hit2)
270 Dy <= (err_y_1 + err_y_2)
271 and Dz <= 2 * (err_z_1 + err_z_2)
272 and ((Dx <= (err_x_1 + err_x_2)
and Dz > 0.0)
or (Dx == 0))
274 list_neighbours.append(hit2)
279 Dx <= (err_x_1 + err_x_2)
280 and Dz <= 6 * (err_z_1 + err_z_2)
281 and ((Dy <= (err_y_1 + err_y_2)
and Dz > 0.0)
or (Dy == 0))
283 list_neighbours.append(hit2)
285 Dy <= (err_y_1 + err_y_2)
286 and Dz <= 6 * (err_z_1 + err_z_2)
287 and ((Dx <= (err_x_1 + err_x_2)
and Dz > 0.0)
or (Dx == 0))
289 list_neighbours.append(hit2)
291 print(
"-- _get_neighbours: ERROR: step not defined")
293 return list_neighbours
None __init__(self, name, intree, outtree=None)
int _get_cluster_energy(self, list_hits)
None _reconstruct_clusters(self)
def _get_subclusters_excluding_fragments(self)
def _get_neighbours(self, hit)