1
2
3
4 package org.lcsim.recon.tracking.digitization.sisim;
5
6 import java.util.ArrayList;
7 import java.util.HashSet;
8 import java.util.List;
9 import java.util.Map.Entry;
10 import java.util.Random;
11 import java.util.Set;
12 import java.util.SortedMap;
13 import java.util.TreeMap;
14 import org.apache.commons.math.MathException;
15 import org.apache.commons.math.distribution.BinomialDistribution;
16 import org.apache.commons.math.distribution.BinomialDistributionImpl;
17 import org.apache.commons.math.distribution.NormalDistribution;
18 import org.apache.commons.math.distribution.NormalDistributionImpl;
19 import org.lcsim.detector.tracker.silicon.SiSensorElectrodes;
20 import org.lcsim.event.RawTrackerHit;
21 import org.lcsim.math.probability.Erf;
22 import org.lcsim.recon.tracking.digitization.sisim.ReadoutChip.ReadoutChannel;
23
24
25
26
27
28
29
30
31
32
33
34 public class DigitalReadoutChip implements ReadoutChip {
35
36 private static Random _random = new Random();
37 private static NormalDistribution _gaussian = new NormalDistributionImpl(0.0, 1.0);
38 private static BinomialDistribution _binomial = new BinomialDistributionImpl(1, 1);
39 private double _noise_threshold;
40 private double _neighbor_threshold;
41 private DigitalChannel _channel = new DigitalChannel();
42
43
44 public DigitalReadoutChip() {
45 }
46
47
48
49
50
51
52
53 public void setNoiseIntercept(double noise_intercept) {
54 _channel.setNoiseIntercept(noise_intercept);
55 }
56
57
58
59
60
61
62
63 public void setNoiseSlope(double noise_slope) {
64 _channel.setNoiseSlope(noise_slope);
65 }
66
67
68
69
70
71
72 public void setNoiseThreshold(double noise_threshold) {
73 _noise_threshold = noise_threshold;
74 _channel.setNoiseThreshold(noise_threshold);
75 }
76
77
78
79
80
81
82
83 public void setNeighborThreshold(double neighbor_threshold) {
84 _neighbor_threshold = neighbor_threshold;
85 }
86
87
88
89
90
91
92 public void setConversionConstant(int adc_for_hit) {
93 _channel.setConversionConstant(adc_for_hit);
94 }
95
96
97
98
99
100
101
102
103
104 public DigitalChannel getChannel(int channel_number) {
105 return _channel;
106 }
107
108
109
110
111
112
113
114
115
116 public SortedMap<Integer, List<Integer>> readout(SiElectrodeDataCollection data, SiSensorElectrodes electrodes) {
117
118
119
120 if (data == null) {
121 data = new SiElectrodeDataCollection();
122 }
123
124
125 addNoise(data, electrodes);
126
127
128
129 return digitize(data, electrodes);
130 }
131
132
133
134
135
136
137
138
139
140 public double decodeCharge(RawTrackerHit hit) {
141
142
143 int adc = hit.getADCValues()[0];
144
145
146
147 if (adc > 0) return _noise_threshold;
148 else return 0.;
149 }
150
151
152
153
154
155
156
157
158 public int decodeTime(RawTrackerHit hit) {
159 return 0;
160 }
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175 private SortedMap<Integer, List<Integer>> digitize(SiElectrodeDataCollection data,
176 SiSensorElectrodes electrodes) {
177
178
179 SortedMap<Integer, List<Integer>> chip_data = new TreeMap<Integer, List<Integer>>();
180
181
182 for (Integer channel : data.keySet()) {
183
184
185 SiElectrodeData eldata = data.get(channel);
186
187
188 double charge = eldata.getCharge();
189
190
191 if (charge < _neighbor_threshold) continue;
192
193
194 if (charge < _noise_threshold) {
195
196
197 boolean nbrhit = false;
198 for (Integer nbr : electrodes.getNearestNeighborCells(channel)) {
199
200
201 SiElectrodeData nbrdata = data.get(nbr);
202 if (nbrdata == null) continue;
203
204
205 if (nbrdata.getCharge() >= _noise_threshold) {
206 nbrhit = true;
207 break;
208 }
209 }
210
211
212 if (!nbrhit) continue;
213 }
214
215
216 int adc = getChannel(channel).computeAdcValue(charge);
217 if (adc <= 0) {
218 continue;
219 }
220
221
222
223 List<Integer> channel_data = new ArrayList<Integer>();
224 channel_data.add(adc);
225
226
227 chip_data.put(channel, channel_data);
228 }
229
230 return chip_data;
231 }
232
233
234
235
236
237
238
239 private void addNoise(SiElectrodeDataCollection data, SiSensorElectrodes electrodes) {
240
241
242
243 for (Entry datum : data.entrySet()) {
244
245
246 int channel = (Integer) datum.getKey();
247 SiElectrodeData eldata = (SiElectrodeData) datum.getValue();
248
249
250 double noise = getChannel(channel).computeNoise(electrodes.getCapacitance(channel));
251
252
253 int noise_charge = (int) Math.round(_random.nextGaussian() * noise);
254 eldata.addCharge(noise_charge);
255 }
256
257
258
259
260 int nelectrodes = electrodes.getNCells();
261 int nelectrodes_empty = nelectrodes - data.size();
262
263
264 double noiseRMS = _channel.computeNoise(electrodes.getCapacitance());
265 double normalized_integration_limit = _noise_threshold / noiseRMS;
266
267
268 double integral = Erf.phic(normalized_integration_limit);
269 int nchannels_throw = drawBinomial(nelectrodes_empty, integral);
270
271
272 for (int ithrow = 0; ithrow < nchannels_throw; ithrow++) {
273
274 int channel = _random.nextInt(nelectrodes);
275 while (data.keySet().contains(channel)) {
276 channel = _random.nextInt(nelectrodes);
277 }
278
279
280 double noise = getChannel(channel).computeNoise(electrodes.getCapacitance(channel));
281
282
283 int charge = (int) Math.round(drawGaussianAboveThreshold(integral) * noise);
284 data.add(channel, new SiElectrodeData(charge));
285 }
286
287
288
289 nchannels_throw = 1;
290 while (nchannels_throw > 0) {
291
292
293
294 Set<Integer> neighbors = new HashSet<Integer>();
295 for (int channel : data.keySet()) {
296 neighbors.addAll(electrodes.getNearestNeighborCells(channel));
297 }
298 neighbors.removeAll(data.keySet());
299
300 nelectrodes_empty = neighbors.size();
301
302
303 normalized_integration_limit = _neighbor_threshold / noiseRMS;
304
305 integral = Erf.phic(normalized_integration_limit);
306 nchannels_throw = drawBinomial(nelectrodes_empty, integral);
307
308
309 for (int ithrow = 0; ithrow < nchannels_throw; ithrow++) {
310
311 List<Integer> neighbors_list = new ArrayList<Integer>(neighbors);
312
313 int channel = neighbors_list.get(_random.nextInt(nelectrodes_empty));
314
315 while (data.keySet().contains(channel)) {
316 channel = neighbors_list.get(_random.nextInt(nelectrodes_empty));
317 }
318
319 double noise = getChannel(channel).computeNoise(electrodes.getCapacitance(channel));
320
321
322
323 int charge = (int) Math.round(drawGaussianAboveThreshold(integral) * noise);
324 data.add(channel, new SiElectrodeData(charge));
325 }
326
327 }
328
329 }
330
331 public static int drawBinomial(int ntrials, double probability) {
332 _binomial.setNumberOfTrials(ntrials);
333 _binomial.setProbabilityOfSuccess(probability);
334
335 int nsuccess = 0;
336 try {
337 nsuccess = _binomial.inverseCumulativeProbability(_random.nextDouble());
338 } catch (MathException exception) {
339 throw new RuntimeException("Kpix failed to calculate inverse cumulative probability of binomial!");
340 }
341 return nsuccess;
342 }
343
344
345
346
347
348
349
350
351 public static double drawGaussianAboveThreshold(double prob_above_threshold) {
352 double draw, cumulative_probability;
353
354 draw = prob_above_threshold * _random.nextDouble();
355 cumulative_probability = 1.0 - prob_above_threshold + draw;
356
357 assert cumulative_probability < 1.0 : "cumulProb=" + cumulative_probability + ", draw=" + draw + ", probAboveThreshold=" + prob_above_threshold;
358 assert cumulative_probability >= 0.0 : "cumulProb=" + cumulative_probability + ", draw=" + draw + ", probAboveThreshold=" + prob_above_threshold;
359
360 double gaussian_random = 0;
361 try {
362 gaussian_random = _gaussian.inverseCumulativeProbability(cumulative_probability);
363 } catch (MathException e) {
364 System.out.println("MathException caught: " + e);
365 }
366
367 return gaussian_random;
368 }
369
370
371
372
373 private class DigitalChannel implements ReadoutChannel {
374
375 private double _noise_intercept = 0.;
376 private double _noise_slope = 0.;
377 private double _noise_threshold = 100.;
378 private int _adc_for_hit = 100;
379
380
381
382
383
384
385 private void setConversionConstant(int adc_for_hit) {
386 _adc_for_hit = adc_for_hit;
387 }
388
389
390
391
392
393
394 private double getConversionConstant() {
395 return _adc_for_hit;
396 }
397
398
399
400
401
402
403 private void setNoiseIntercept(double noise_intercept) {
404 _noise_intercept = noise_intercept;
405 }
406
407
408
409
410
411
412 private void setNoiseSlope(double noise_slope) {
413 _noise_slope = noise_slope;
414 }
415
416
417
418
419
420
421 private void setNoiseThreshold(double noise_threshold) {
422 _noise_threshold = noise_threshold;
423 }
424
425
426
427
428
429
430
431 public double computeNoise(double capacitance) {
432 return _noise_intercept + _noise_slope * capacitance;
433 }
434
435
436
437
438
439
440
441 private int computeAdcValue(double charge) {
442
443
444
445 int adc = 0;
446 if (charge > _noise_threshold) adc = _adc_for_hit;
447
448
449 if (adc < 0) {
450 adc = 0;
451 }
452
453
454 if (adc > 32767) {
455 adc = 32767;
456 }
457
458 return adc;
459 }
460 }
461 }