1 package org.lcsim.recon.cluster.fixedcone;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.Map;
7 import org.lcsim.event.CalorimeterHit;
8 import org.lcsim.event.Cluster;
9 import org.lcsim.event.util.CalorimeterHitEsort;
10 import org.lcsim.event.base.BaseCluster;
11 import org.lcsim.recon.cluster.util.Clusterer;
12 import org.lcsim.recon.cluster.util.ClusterESort;
13 import org.lcsim.recon.cluster.util.FixedConeClusterPropertyCalculator;
14
15 import org.lcsim.util.fourvec.Lorentz4Vector;
16 import org.lcsim.util.fourvec.Momentum4Vector;
17
18 import org.lcsim.spacegeom.SpacePoint;
19 import org.lcsim.spacegeom.CartesianPoint;
20
21 import static java.lang.Math.sin;
22 import static java.lang.Math.cos;
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 public class FixedConeClusterer implements Clusterer {
47
48 private double _radius;
49 private double _seedEnergy;
50 private double _minEnergy;
51 private int _numLayers;
52 private double _samplingFraction;
53 private double[] _layerEnergy;
54 private boolean _radiusSetFlag;
55 private double[] _coneFunc;
56 private double[] _cutFunc;
57 private static final double PI = Math.PI;
58 private static final double TWOPI = 2. * PI;
59 public FixedConeClusterPropertyCalculator _clusterPropertyCalculator;
60 private FixedConeDistanceMetric _dm;
61
62 public enum FixedConeDistanceMetric {
63
64 DOTPRODUCT, DPHIDCOSTHETA, DPHIDTHETA
65 }
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 public FixedConeClusterer(double seed, double minE, double[] coneFunc, double[] cutFunc, FixedConeDistanceMetric distMetric) {
82
83 _seedEnergy = seed;
84 _minEnergy = minE;
85 _dm = distMetric;
86
87 _coneFunc = coneFunc;
88 _cutFunc = cutFunc;
89 _radiusSetFlag = false;
90
91 }
92
93 public FixedConeClusterer(double seed, double minE, FixedConeDistanceMetric distMetric) {
94
95 _seedEnergy = seed;
96 _minEnergy = minE;
97 _dm = distMetric;
98
99
100 _coneFunc = new double[2];
101 _coneFunc[0] = 0.011347;
102 _coneFunc[1] = 0.043117;
103 _cutFunc = new double[2];
104 _cutFunc[0] = 3.2;
105 _cutFunc[1] = 2.0;
106 _radiusSetFlag = false;
107
108 }
109
110 public FixedConeClusterer(double radius, double seed, double minE, FixedConeDistanceMetric distMetric) {
111 _radius = radius;
112
113 _seedEnergy = seed;
114 _minEnergy = minE;
115 _dm = distMetric;
116
117 _radiusSetFlag = true;
118 }
119
120
121
122
123
124
125
126
127 public FixedConeClusterer(double radius, double seed, double minE) {
128 this(radius, seed, minE, FixedConeDistanceMetric.DOTPRODUCT);
129 }
130
131 public void setPredictFunc(double[] coneFunc, double[] cutFunc) {
132 _coneFunc = coneFunc;
133 _cutFunc = cutFunc;
134 _radiusSetFlag = false;
135 }
136
137 public void setRadius(double radius) {
138 _radius = radius;
139 _radiusSetFlag = true;
140 }
141
142 public void setSeed(double seed) {
143 _seedEnergy = seed;
144 }
145
146 public void setMinE(double minE) {
147 _minEnergy = minE;
148 }
149
150
151
152
153 public List<Cluster> createClusters(Map<Long, CalorimeterHit> in) {
154 List<CalorimeterHit> list = new ArrayList(in.values());
155 return createClusters(list);
156 }
157
158
159
160
161 public List<Cluster> createClusters(List<CalorimeterHit> in) {
162
163 List<Cluster> out = new ArrayList<Cluster>();
164 List<Double> radius = new ArrayList<Double>();
165 _clusterPropertyCalculator = new FixedConeClusterPropertyCalculator();
166
167
168
169 Collections.sort(in, new CalorimeterHitEsort());
170
171 int nclus = 0;
172 int size = in.size();
173 boolean[] used = new boolean[size];
174
175
176 for (int i = 0; i < size; ++i) {
177 if (!used[i]) {
178 CalorimeterHit p = in.get(i);
179 double Eseed = p.getCorrectedEnergy();
180 double clusterCut = 0.0;
181 if (!_radiusSetFlag) {
182 clusterCut = _cutFunc[0] * Eseed + _cutFunc[1] * Eseed * Eseed;
183 }
184 if (p.getCorrectedEnergy() > _seedEnergy)
185 {
186 if (!_radiusSetFlag) {
187 _radius = _coneFunc[0] * Math.log(p.getCorrectedEnergy()) + _coneFunc[1];
188
189 }
190 if (_radius < 0.0) {
191 System.out.println("Engergy = " + p.getCorrectedEnergy() + ",Radius = " + _radius);
192 break;
193 }
194 double rsquared = _radius * _radius;
195 double[] pos = p.getPosition();
196 SpacePoint sp = new CartesianPoint(pos);
197 double phi1 = sp.phi();
198 double sphi1 = sin(phi1);
199 double cphi1 = cos(phi1);
200 double theta1 = sp.theta();
201 double stheta1 = sin(theta1);
202 double ctheta1 = cos(theta1);
203
204
205
206 double cellE = p.getCorrectedEnergy();
207
208
209
210 double px = cellE * cphi1 * stheta1;
211 double py = cellE * sphi1 * stheta1;
212 double pz = cellE * ctheta1;
213
214 Lorentz4Vector sum = new Momentum4Vector(px, py, pz, cellE);
215 double phiseed = sum.phi();
216 double thetaseed = sum.theta();
217
218
219 List<CalorimeterHit> members = new ArrayList<CalorimeterHit>();
220 members.add(p);
221
222
223 for (int j = i + 1; j < size; ++j) {
224 if (!used[j]) {
225 CalorimeterHit p2 = in.get(j);
226 SpacePoint sp2 = new CartesianPoint(p2.getPosition());
227 double phi = sp2.phi();
228 double theta = sp2.theta();
229 double dphi = phi - phiseed;
230
231 if (dphi < -PI) {
232 dphi += TWOPI;
233 }
234 if (dphi > PI) {
235 dphi -= TWOPI;
236 }
237 double dtheta = theta - thetaseed;
238 double R2 = 0;
239 boolean cond = false;
240
241 switch (_dm) {
242 case DPHIDCOSTHETA:
243
244 double dcostheta = cos(theta) - cos(thetaseed);
245 R2 = dphi * dphi + dcostheta * dcostheta;
246 cond = (R2 < rsquared);
247 break;
248 case DPHIDTHETA:
249
250 R2 = dphi * dphi + dtheta * dtheta;
251 cond = (R2 < rsquared);
252 break;
253 case DOTPRODUCT:
254 default:
255
256 double dotp = Math.sin(theta) * Math.sin(thetaseed) *
257 (sin(phi) * sin(phiseed) +
258 cos(phi) * cos(phiseed)) +
259 cos(theta) * cos(thetaseed);
260 cond = (dotp > cos(_radius));
261 break;
262 }
263
264 if (cond) {
265
266 cellE = p2.getCorrectedEnergy();
267 px = cellE * cos(phi) * sin(theta);
268 py = cellE * sin(phi) * sin(theta);
269 pz = cellE * cos(theta);
270 sum.plusEquals(new Momentum4Vector(px, py, pz, cellE));
271 members.add(p2);
272
273 used[j] = true;
274
275
276 phiseed = sum.phi();
277 thetaseed = sum.theta();
278 }
279 }
280 }
281
282
283
284 if (sum.E() > clusterCut)
285 {
286 BaseCluster clus = new BaseCluster();
287 clus.setPropertyCalculator(_clusterPropertyCalculator);
288 for (CalorimeterHit hit : members) {
289 clus.addHit(hit);
290 }
291 out.add(clus);
292 radius.add(_radius);
293 nclus++;
294 }
295
296 }
297 }
298
299 }
300
301 if (nclus > 1) {
302
303 Collections.sort(out, new ClusterESort());
304
305
306 for (int i = 0; i < out.size(); ++i) {
307 for (int j = i + 1; j < out.size(); ++j) {
308 double dTheta = dTheta(out.get(i), out.get(j));
309 if (dTheta < radius.get(i) + radius.get(j)) {
310 resolve((BaseCluster) (out.get(i)), radius.get(i), (BaseCluster) (out.get(j)), radius.get(j));
311 }
312 }
313 }
314 }
315
316
317 return out;
318 }
319
320
321
322
323
324
325
326
327 public double dTheta(Cluster c1, Cluster c2) {
328 _clusterPropertyCalculator.calculateProperties(c1.getCalorimeterHits());
329 Lorentz4Vector v1 = _clusterPropertyCalculator.vector();
330 _clusterPropertyCalculator.calculateProperties(c2.getCalorimeterHits());
331 Lorentz4Vector v2 = _clusterPropertyCalculator.vector();
332 double costheta = (v1.vec3dot(v2)) / (v1.p() * v2.p());
333 return Math.acos(costheta);
334 }
335
336
337
338
339
340
341
342
343
344 public void resolve(BaseCluster c1, double coneR1, BaseCluster c2, double coneR2) {
345
346
347
348
349 _clusterPropertyCalculator.calculateProperties(c1.getCalorimeterHits());
350 Lorentz4Vector v1 = _clusterPropertyCalculator.vector();
351 double phi1 = v1.phi();
352 double theta1 = v1.theta();
353 _clusterPropertyCalculator.calculateProperties(c2.getCalorimeterHits());
354 Lorentz4Vector v2 = _clusterPropertyCalculator.vector();
355 double phi2 = v2.phi();
356 double theta2 = v2.theta();
357 List<CalorimeterHit> cells1 = c1.getCalorimeterHits();
358 List<CalorimeterHit> cells2 = c2.getCalorimeterHits();
359 int size2 = cells2.size();
360 List<CalorimeterHit> swap = new ArrayList<CalorimeterHit>();
361
362
363 for (int i = 0; i < cells1.size(); ++i) {
364 CalorimeterHit p2 = (CalorimeterHit) cells1.get(i);
365 SpacePoint sp2 = new CartesianPoint(p2.getPosition());
366
367
368 double phi = sp2.phi();
369 double theta = sp2.theta();
370
371 double dphi1 = phi - phi1;
372 if (dphi1 < -PI) {
373 dphi1 += TWOPI;
374 }
375 if (dphi1 > PI) {
376 dphi1 -= TWOPI;
377 }
378 double dtheta1 = theta - theta1;
379 double R1 = dphi1 * dphi1 + (dtheta1) * (dtheta1);
380
381 double dphi2 = phi - phi2;
382 if (dphi2 < -PI) {
383 dphi2 += TWOPI;
384 }
385 if (dphi2 > PI) {
386 dphi2 -= TWOPI;
387 }
388 double dtheta2 = theta - theta2;
389 double R2 = dphi2 * dphi2 + (dtheta2) * (dtheta2);
390
391 if (R2 / R1 < coneR2 / coneR1) {
392 swap.add(p2);
393 }
394 }
395 for (CalorimeterHit h : swap) {
396 c1.removeHit(h);
397 c2.addHit(h);
398 }
399 swap = new ArrayList<CalorimeterHit>();
400
401
402 for (int i = 0; i < size2; ++i) {
403 CalorimeterHit p2 = (CalorimeterHit) cells2.get(i);
404
405
406 SpacePoint sp2 = new CartesianPoint(p2.getPosition());
407 double phi = sp2.phi();
408 double theta = sp2.theta();
409
410 double dphi1 = phi - phi1;
411 if (dphi1 < -PI) {
412 dphi1 += TWOPI;
413 }
414 if (dphi1 > PI) {
415 dphi1 -= TWOPI;
416 }
417 double dtheta1 = theta - theta1;
418 double R1 = dphi1 * dphi1 + (dtheta1) * (dtheta1);
419
420 double dphi2 = phi - phi2;
421 if (dphi2 < -PI) {
422 dphi2 += TWOPI;
423 }
424 if (dphi2 > PI) {
425 dphi2 -= TWOPI;
426 }
427 double dtheta2 = theta - theta2;
428 double R2 = dphi2 * dphi2 + (dtheta2) * (dtheta2);
429
430 if (R1 / R2 < coneR1 / coneR2) {
431 swap.add(p2);
432 }
433 }
434 for (CalorimeterHit h : swap) {
435 c2.removeHit(h);
436 c1.addHit(h);
437 }
438
439 c1.calculateProperties();
440 c2.calculateProperties();
441 }
442
443 public String toString() {
444 return "FixedConeClusterer with radius " + _radius + " seed Energy " + _seedEnergy + " minimum energy " + _minEnergy + "distance metric " + _dm;
445 }
446 }
447