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
35
36
37
38
39
40 public class BasicReadoutChip implements ReadoutChip
41 {
42
43 private static Random _random = new Random();
44 private static NormalDistribution _gaussian = new NormalDistributionImpl(0.0, 1.0);
45 private static BinomialDistribution _binomial = new BinomialDistributionImpl(1, 1);
46 private double _noise_threshold = 4;
47 private double _neighbor_threshold = 4;
48 private BasicChannel _channel = new BasicChannel();
49 private ADC _adc = new ADC();
50
51
52 public BasicReadoutChip()
53 {
54 }
55
56
57
58
59
60
61
62 public void setNoiseIntercept(double noise_intercept)
63 {
64 _channel.setNoiseIntercept(noise_intercept);
65 }
66
67
68
69
70
71
72
73 public void setNoiseSlope(double noise_slope)
74 {
75 _channel.setNoiseSlope(noise_slope);
76 }
77
78
79
80
81
82
83 public void setNoiseThreshold(double noise_threshold)
84 {
85 _noise_threshold = noise_threshold;
86 }
87
88
89
90
91
92
93
94 public void setNeighborThreshold(double neighbor_threshold)
95 {
96 _neighbor_threshold = neighbor_threshold;
97 }
98
99
100
101
102
103
104 public void setNbits(int nbits)
105 {
106 getADC().setNbits(nbits);
107 }
108
109
110
111
112
113
114 public void setDynamicRange(double dynamic_range)
115 {
116 getADC().setDynamicRange(dynamic_range);
117 }
118
119
120
121
122
123
124
125
126
127 public BasicChannel getChannel(int channel_number)
128 {
129 return _channel;
130 }
131
132 private ADC getADC()
133 {
134 return _adc;
135 }
136
137
138
139
140
141
142
143
144
145 public SortedMap<Integer, List<Integer>> readout(SiElectrodeDataCollection data, SiSensorElectrodes electrodes)
146 {
147
148
149
150 if (data == null)
151 {
152 data = new SiElectrodeDataCollection();
153 }
154
155
156 addNoise(data, electrodes);
157
158
159
160 return digitize(data, electrodes);
161 }
162
163
164
165
166
167
168
169 public double decodeCharge(RawTrackerHit hit)
170 {
171 return getADC().decodeCharge(hit.getADCValues()[0]);
172 }
173
174
175
176
177
178
179
180
181 public int decodeTime(RawTrackerHit hit)
182 {
183 return 0;
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199 private SortedMap<Integer, List<Integer>> digitize(SiElectrodeDataCollection data,
200 SiSensorElectrodes electrodes)
201 {
202
203
204 SortedMap<Integer, List<Integer>> chip_data = new TreeMap<Integer, List<Integer>>();
205
206
207 for (Integer channel : data.keySet())
208 {
209
210
211 SiElectrodeData eldata = data.get(channel);
212
213
214 double charge = eldata.getCharge();
215
216
217 double noiseRMS = getChannel(channel).computeNoise(electrodes.getCapacitance(channel));
218
219
220 if (charge < _neighbor_threshold*noiseRMS)
221 {
222 continue;
223 }
224
225
226 if (charge < _noise_threshold*noiseRMS)
227 {
228
229
230 boolean nbrhit = false;
231 for (Integer nbr : electrodes.getNearestNeighborCells(channel))
232 {
233
234
235 SiElectrodeData nbrdata = data.get(nbr);
236 if (nbrdata == null)
237 {
238 continue;
239 }
240
241
242 if (nbrdata.getCharge() >= _noise_threshold)
243 {
244 nbrhit = true;
245 break;
246 }
247 }
248
249
250 if (!nbrhit)
251 {
252 continue;
253 }
254 }
255
256
257 int adc = getADC().convert(charge);
258 if (adc <= 0)
259 {
260 continue;
261 }
262
263
264
265 List<Integer> channel_data = new ArrayList<Integer>();
266 channel_data.add(adc);
267
268
269 chip_data.put(channel, channel_data);
270 }
271
272 return chip_data;
273 }
274
275
276
277
278
279
280
281 private void addNoise(SiElectrodeDataCollection data, SiSensorElectrodes electrodes)
282 {
283
284
285
286 for (Entry datum : data.entrySet())
287 {
288
289
290 int channel = (Integer) datum.getKey();
291 SiElectrodeData eldata = (SiElectrodeData) datum.getValue();
292
293
294 double noise = getChannel(channel).computeNoise(electrodes.getCapacitance(channel));
295
296
297 int noise_charge = (int) Math.round(_random.nextGaussian() * noise);
298 eldata.addCharge(noise_charge);
299 }
300
301
302
303
304 int nelectrodes = electrodes.getNCells();
305 int nelectrodes_empty = nelectrodes - data.size();
306
307
308 double normalized_integration_limit = _noise_threshold;
309
310
311 double integral = Erf.phic(normalized_integration_limit);
312 int nchannels_throw = drawBinomial(nelectrodes_empty, integral);
313
314
315 for (int ithrow = 0; ithrow < nchannels_throw; ithrow++)
316 {
317
318 int channel = _random.nextInt(nelectrodes);
319 while (data.keySet().contains(channel))
320 {
321 channel = _random.nextInt(nelectrodes);
322 }
323
324
325 double noise = getChannel(channel).computeNoise(electrodes.getCapacitance(channel));
326
327
328 int charge = (int) Math.round(drawGaussianAboveThreshold(integral) * noise);
329 data.add(channel, new SiElectrodeData(charge));
330 }
331
332
333
334 nchannels_throw = 1;
335 while (nchannels_throw > 0)
336 {
337
338
339
340 Set<Integer> neighbors = new HashSet<Integer>();
341 for (int channel : data.keySet())
342 {
343 neighbors.addAll(electrodes.getNearestNeighborCells(channel));
344 }
345 neighbors.removeAll(data.keySet());
346
347 nelectrodes_empty = neighbors.size();
348
349
350 normalized_integration_limit = _neighbor_threshold;
351
352 integral = Erf.phic(normalized_integration_limit);
353 nchannels_throw = drawBinomial(nelectrodes_empty, integral);
354
355
356 for (int ithrow = 0; ithrow < nchannels_throw; ithrow++)
357 {
358
359 List<Integer> neighbors_list = new ArrayList<Integer>(neighbors);
360
361 int channel = neighbors_list.get(_random.nextInt(nelectrodes_empty));
362
363 while (data.keySet().contains(channel))
364 {
365 channel = neighbors_list.get(_random.nextInt(nelectrodes_empty));
366 }
367
368 double noise = getChannel(channel).computeNoise(electrodes.getCapacitance(channel));
369
370
371
372 int charge = (int) Math.round(drawGaussianAboveThreshold(integral) * noise);
373 data.add(channel, new SiElectrodeData(charge));
374 }
375
376 }
377
378 }
379
380 public static int drawBinomial(int ntrials, double probability)
381 {
382 _binomial.setNumberOfTrials(ntrials);
383 _binomial.setProbabilityOfSuccess(probability);
384
385 int nsuccess = 0;
386 try
387 {
388 nsuccess = _binomial.inverseCumulativeProbability(_random.nextDouble());
389 } catch (MathException exception)
390 {
391 throw new RuntimeException("BasicReadoutChip failed to calculate inverse cumulative probability of binomial!");
392 }
393 return nsuccess;
394 }
395
396
397
398
399
400 public static double drawGaussianAboveThreshold(double prob_above_threshold)
401 {
402 double draw, cumulative_probability;
403
404 draw = prob_above_threshold * _random.nextDouble();
405 cumulative_probability = 1.0 - prob_above_threshold + draw;
406
407 assert cumulative_probability < 1.0 : "cumulProb=" + cumulative_probability + ", draw=" + draw + ", probAboveThreshold=" + prob_above_threshold;
408 assert cumulative_probability >= 0.0 : "cumulProb=" + cumulative_probability + ", draw=" + draw + ", probAboveThreshold=" + prob_above_threshold;
409
410 double gaussian_random = 0;
411 try
412 {
413 gaussian_random = _gaussian.inverseCumulativeProbability(cumulative_probability);
414 } catch (MathException e)
415 {
416 System.out.println("MathException caught: " + e);
417 }
418
419 return gaussian_random;
420 }
421
422
423
424
425
426
427
428
429
430 private class BasicChannel implements ReadoutChannel
431 {
432
433 private double _noise_intercept = 0.;
434 private double _noise_slope = 0.;
435
436
437
438
439
440
441 private void setNoiseIntercept(double noise_intercept)
442 {
443 _noise_intercept = noise_intercept;
444 }
445
446
447
448
449
450
451 private void setNoiseSlope(double noise_slope)
452 {
453 _noise_slope = noise_slope;
454 }
455
456
457
458
459
460
461
462 public double computeNoise(double capacitance)
463 {
464 return _noise_intercept + _noise_slope * capacitance;
465 }
466 }
467
468
469
470
471 private class ADC
472 {
473
474 private int _nbits = 8;
475 private double _dynamic_range = 20.;
476
477
478
479
480
481
482 private void setNbits(int nbits)
483 {
484 _nbits = nbits;
485 }
486
487
488
489
490
491
492 private void setDynamicRange(double dynamic_range)
493 {
494 _dynamic_range = dynamic_range;
495 }
496
497
498
499
500
501
502 private int maxADCValue()
503 {
504 return (int) Math.pow(2, _nbits) - 1;
505 }
506
507
508
509
510
511
512 private double conversionConstant()
513 {
514 return maxADCValue() / _dynamic_range;
515 }
516
517
518
519
520
521
522 public int convert(double charge)
523 {
524 if (_nbits != 1)
525 {
526 return Math.max(0, Math.min(maxADCValue(), (int) Math.floor(charge * 1.602e-4 * conversionConstant())));
527 }
528 else
529 {
530 if (charge <= 0.0)
531 {
532 return 0;
533 }
534 else
535 {
536 return 1;
537 }
538 }
539 }
540
541
542
543
544
545
546 public double decodeCharge(int adc_value)
547 {
548 if (_nbits != 1)
549 {
550 return (adc_value + 0.5) / (1.602e-4 * conversionConstant());
551 }
552 else
553 {
554 return adc_value*_dynamic_range;
555 }
556
557 }
558 }
559 }