1 package org.hps.recon.tracking;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.logging.Level;
6 import java.util.logging.Logger;
7
8 import org.apache.commons.math3.linear.Array2DRowRealMatrix;
9 import org.apache.commons.math3.linear.ArrayRealVector;
10 import org.apache.commons.math3.linear.CholeskyDecomposition;
11 import org.apache.commons.math3.linear.DecompositionSolver;
12 import org.apache.commons.math3.linear.NonPositiveDefiniteMatrixException;
13 import org.apache.commons.math3.linear.RealMatrix;
14 import org.apache.commons.math3.linear.RealVector;
15 import org.apache.commons.math3.special.Gamma;
16 import org.freehep.math.minuit.FCNBase;
17 import org.freehep.math.minuit.FunctionMinimum;
18 import org.freehep.math.minuit.MnSimplex;
19 import org.freehep.math.minuit.MnUserParameters;
20
21
22 import org.hps.readout.svt.HPSSVTConstants;
23 import org.lcsim.detector.tracker.silicon.HpsSiSensor;
24 import org.lcsim.event.RawTrackerHit;
25
26
27
28
29
30
31
32 public class ShaperLinearFitAlgorithm implements ShaperFitAlgorithm, FCNBase {
33
34 private final int nPulses;
35 final double[] amplitudes;
36 final double[] amplitudeErrors;
37 private double pedestal;
38
39 private HpsSiSensor sensor;
40 private int channel;
41 private PulseShape shape;
42 private final double[] sigma = new double[HPSSVTConstants.TOTAL_NUMBER_OF_SAMPLES];
43 private final double[] y = new double[HPSSVTConstants.TOTAL_NUMBER_OF_SAMPLES];
44 private int firstUsedSample;
45 private int nUsedSamples;
46 private int firstFittedPulse;
47 private int nFittedPulses;
48 private boolean fitPedestal = false;
49 private boolean debug = false;
50 private static final Logger minuitLoggger = Logger.getLogger("org.freehep.math.minuit");
51
52 public ShaperLinearFitAlgorithm(int nPulses) {
53 this.nPulses = nPulses;
54 amplitudes = new double[nPulses];
55 amplitudeErrors = new double[nPulses];
56 }
57
58 @Override
59 public void setDebug(boolean debug) {
60 this.debug = debug;
61 if (debug) {
62 minuitLoggger.setLevel(Level.INFO);
63 } else {
64 minuitLoggger.setLevel(Level.OFF);
65 }
66 }
67
68 public void setFitPedestal(boolean fitPedestal) {
69 this.fitPedestal = fitPedestal;
70 }
71
72 public boolean fitsPedestal() {
73 return fitPedestal;
74 }
75
76 public double getPedestal() {
77 return pedestal;
78 }
79
80 @Override
81
82 public Collection<ShapeFitParameters> fitShape(RawTrackerHit rth, PulseShape shape) {
83 short[] samples = rth.getADCValues();
84 sensor = (HpsSiSensor) rth.getDetectorElement();
85 channel = rth.getIdentifierFieldValue("strip");
86 this.shape = shape;
87 shape.setParameters(channel, sensor);
88 return fitShape(samples);
89
90 }
91
92 public Collection<ShapeFitParameters> fitShape(short[] samples) {
93
94
95 double[] signal = new double[HPSSVTConstants.TOTAL_NUMBER_OF_SAMPLES];
96
97 for (int i = 0; i < samples.length; i++) {
98
99 signal[i] = samples[i] - sensor.getPedestal(channel, i);
100
101 sigma[i] = sensor.getNoise(channel, i);
102 }
103
104
105
106
107 firstUsedSample = 0;
108 nUsedSamples = samples.length;
109 firstFittedPulse = 0;
110 nFittedPulses = nPulses;
111
112 if (debug) {
113 System.out.print("Signal:\t");
114 for (int i = 0; i < signal.length; i++) {
115 System.out.format("%f\t", signal[i]);
116 }
117 System.out.println();
118 }
119
120 FunctionMinimum min = doRecursiveFit(signal);
121
122
123
124
125
126
127
128
129 double chisq = evaluateMinimum(min);
130
131 ArrayList<ShapeFitParameters> fits = new ArrayList<ShapeFitParameters>();
132
133 for (int i = 0; i < nPulses; i++) {
134 ShapeFitParameters fit = new ShapeFitParameters();
135 fit.setAmp(amplitudes[i]);
136 fit.setAmpErr(amplitudeErrors[i]);
137 if (fitPedestal) {
138 fit.setChiProb(Gamma.regularizedGammaQ(samples.length - 2 * nPulses - 1, chisq));
139 } else {
140 fit.setChiProb(Gamma.regularizedGammaQ(samples.length - 2 * nPulses, chisq));
141 }
142
143 fit.setT0(min.userState().value(i));
144
145 fit.setT0Err(min.userState().error(i));
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171 fits.add(fit);
172 }
173
174 return fits;
175 }
176
177 private FunctionMinimum doRecursiveFit(double[] samples) {
178 if (nFittedPulses == 1) {
179 System.arraycopy(samples, 0, y, 0, samples.length);
180 FunctionMinimum fit = minuitFit(null);
181 return fit;
182 } else {
183 FunctionMinimum bestFit = null;
184 double bestChisq = Double.POSITIVE_INFINITY;
185 double[] fitData = new double[samples.length];
186 for (int split = 1; split < y.length; split++) {
187 if (debug) {
188 System.out.println("Split\t" + split);
189 }
190
191
192 System.arraycopy(samples, 0, fitData, 0, samples.length);
193
194 firstUsedSample = 0;
195 nUsedSamples = split;
196
197 nFittedPulses = 1;
198 FunctionMinimum frontFit;
199 frontFit = doRecursiveFit(fitData);
200 if (debug) {
201 if (fitPedestal) {
202 System.out.format("front fit:\tt0=%f,\tA=%f,\tchisq=%f,\tpedestal=%f\n", frontFit.userState().value(0), amplitudes[firstFittedPulse], frontFit.fval(), pedestal);
203 } else {
204 System.out.format("front fit:\tt0=%f,\tA=%f,\tchisq=%f\n", frontFit.userState().value(0), amplitudes[firstFittedPulse], frontFit.fval());
205 }
206 }
207
208 for (int i = 0; i < samples.length; i++) {
209
210 fitData[i] -= amplitudes[firstFittedPulse] * shape.getAmplitudePeakNorm(HPSSVTConstants.SAMPLING_INTERVAL * i - frontFit.userState().value(0));
211 if (fitPedestal) {
212 fitData[i] -= pedestal;
213 }
214 }
215
216 if (debug) {
217 System.out.print("Subtracted:\t");
218 for (int i = 0; i < fitData.length; i++) {
219 System.out.format("%f\t", fitData[i]);
220 }
221 System.out.println();
222 }
223
224 firstUsedSample = 0;
225 nUsedSamples = y.length;
226
227 firstFittedPulse++;
228 nFittedPulses = nPulses - firstFittedPulse;
229 FunctionMinimum backFit;
230 if (fitPedestal) {
231 fitPedestal = false;
232 backFit = doRecursiveFit(fitData);
233 fitPedestal = true;
234 } else {
235 backFit = doRecursiveFit(fitData);
236 }
237
238 if (debug) {
239 System.out.format("back fit:\tt0=%f,\tA=%f,\tchisq=%f\n", backFit.userState().value(0), amplitudes[firstFittedPulse], backFit.fval());
240 }
241
242
243 System.arraycopy(samples, 0, y, 0, samples.length);
244
245
246 firstFittedPulse--;
247 nFittedPulses++;
248 double[] combinedGuess = new double[nFittedPulses];
249 combinedGuess[0] = frontFit.userState().value(0);
250 for (int i = 0; i < nFittedPulses - 1; i++) {
251 combinedGuess[i + 1] = backFit.userState().value(i);
252 }
253 FunctionMinimum combinedFit = minuitFit(combinedGuess);
254
255 if (debug) {
256 if (fitPedestal) {
257 System.out.format("combined fit:\tt0=%f,\tA=%f,\tt0=%f,\tA=%f,\tchisq=%f,\tpedestal=%f\n", combinedFit.userState().value(0), amplitudes[firstFittedPulse], combinedFit.userState().value(1), amplitudes[firstFittedPulse + 1], combinedFit.fval(), pedestal);
258 } else {
259 System.out.format("combined fit:\tt0=%f,\tA=%f,\tt0=%f,\tA=%f,\tchisq=%f\n", combinedFit.userState().value(0), amplitudes[firstFittedPulse], combinedFit.userState().value(1), amplitudes[firstFittedPulse + 1], combinedFit.fval());
260 }
261 }
262
263 double newchisq = evaluateMinimum(combinedFit);
264
265 if (newchisq < bestChisq) {
266 bestChisq = newchisq;
267 bestFit = combinedFit;
268 }
269 }
270
271
272 if (debug) {
273 if (fitPedestal) {
274 System.out.format("best fit:\tt0=%f,\tA=%f,\tt0=%f,\tA=%f,\tchisq=%f,\tpedestal=%f\n", bestFit.userState().value(0), amplitudes[firstFittedPulse], bestFit.userState().value(1), amplitudes[firstFittedPulse + 1], bestFit.fval(), pedestal);
275 } else {
276 System.out.format("best fit:\tt0=%f,\tA=%f,\tt0=%f,\tA=%f,\tchisq=%f\n", bestFit.userState().value(0), amplitudes[firstFittedPulse], bestFit.userState().value(1), amplitudes[firstFittedPulse + 1], bestFit.fval());
277 }
278 }
279 return bestFit;
280 }
281 }
282
283 private double evaluateMinimum(FunctionMinimum min) {
284 double[] times = new double[nFittedPulses];
285 for (int i = 0; i < nFittedPulses; i++) {
286 times[i] = min.userState().value(i);
287 }
288 return doLinFit(times);
289 }
290
291 private FunctionMinimum minuitFit(double[] guess_t) {
292 if (debug) {
293 System.out.print("y for fit:\t");
294 for (int i = 0; i < y.length; i++) {
295 System.out.format("%f\t", y[i]);
296 }
297 System.out.println();
298 }
299
300 if (nFittedPulses == 1) {
301 double guess = 0;
302
303 int numPositiveSamples = 0;
304 int numBigSamples = 0;
305 int lastUsedSample = firstUsedSample + nUsedSamples - 1;
306 int firstBigSample = Integer.MAX_VALUE;
307 for (int i = 0; i < nUsedSamples; i++) {
308 if (y[firstUsedSample + i] > 0) {
309 numPositiveSamples++;
310 if (y[firstUsedSample + i] > 3.0 * sigma[firstUsedSample + i]) {
311 numBigSamples++;
312 if (firstUsedSample + i < firstBigSample) {
313 firstBigSample = firstUsedSample + i;
314 }
315 }
316 }
317 }
318 boolean made_guess = false;
319 boolean made_bestfit = false;
320 if (nUsedSamples == 1) {
321 if (firstUsedSample == 0) {
322 guess = -500.0;
323 made_bestfit = true;
324 } else {
325 guess = HPSSVTConstants.SAMPLING_INTERVAL * (firstUsedSample - 0.1);
326 made_bestfit = true;
327 }
328 } else if (numPositiveSamples == 1 && y[lastUsedSample] > 0) {
329 guess = HPSSVTConstants.SAMPLING_INTERVAL * (lastUsedSample - 0.1);
330 made_bestfit = true;
331 } else if (numBigSamples == 1 && y[lastUsedSample] > 3.0 * sigma[lastUsedSample] && nUsedSamples > 1 && y[lastUsedSample - 1] < 0) {
332 guess = HPSSVTConstants.SAMPLING_INTERVAL * (lastUsedSample - 0.1);
333 made_bestfit = true;
334 } else if (nUsedSamples == 2) {
335 guess = HPSSVTConstants.SAMPLING_INTERVAL * (firstUsedSample - 0.1);
336 made_guess = true;
337 }
338 if (made_guess || made_bestfit) {
339
340 guess_t = new double[1];
341 guess_t[0] = guess;
342 }
343 }
344
345 MnUserParameters myParams = new MnUserParameters();
346
347 for (int i = 0; i < nFittedPulses; i++) {
348 if (guess_t != null && guess_t.length == nFittedPulses) {
349 myParams.add("time_" + i, guess_t[i], HPSSVTConstants.SAMPLING_INTERVAL, -500.0, (y.length - 1) * HPSSVTConstants.SAMPLING_INTERVAL);
350 } else {
351 myParams.add("time_" + i, (i - 1) * HPSSVTConstants.SAMPLING_INTERVAL, HPSSVTConstants.SAMPLING_INTERVAL, -500.0, (y.length - 1) * HPSSVTConstants.SAMPLING_INTERVAL);
352 }
353 }
354
355 MnSimplex simplex = new MnSimplex(this, myParams, 2);
356 FunctionMinimum min = simplex.minimize(0, 0.001);
357 return min;
358 }
359
360 private double doLinFit(double[] times) {
361 if (times.length != nFittedPulses) {
362 throw new RuntimeException("wrong number of parameters in doLinFit");
363 }
364 int nAmplitudes = fitPedestal ? nFittedPulses + 1 : nFittedPulses;
365 RealMatrix sc_mat = new Array2DRowRealMatrix(nAmplitudes, nUsedSamples);
366 RealVector y_vec = new ArrayRealVector(nUsedSamples);
367 RealVector var_vec = new ArrayRealVector(nUsedSamples);
368
369 for (int j = 0; j < nUsedSamples; j++) {
370 double sigma_j = sigma[firstUsedSample + j];
371
372
373
374
375
376 if (fitPedestal) {
377 sc_mat.setEntry(nFittedPulses, j, 1.0 / sigma_j);
378 }
379 y_vec.setEntry(j, y[firstUsedSample + j] / sigma_j);
380 var_vec.setEntry(j, sigma_j * sigma_j);
381 }
382
383 double[] amplitudez = new double[nUsedSamples];
384 for(int i = 0; i< nFittedPulses; i++){
385 double t0 = HPSSVTConstants.SAMPLING_INTERVAL * firstUsedSample - times[i];
386 shape.getAmplitudesPeakNorm(t0, HPSSVTConstants.SAMPLING_INTERVAL, amplitudez);
387
388 for(int j = 0; j<nUsedSamples; j++){
389
390
391
392
393 sc_mat.setEntry(i, j, amplitudez[j] / sigma[firstUsedSample + j]);
394 }
395 }
396 RealVector a_vec = sc_mat.operate(y_vec);
397 RealMatrix coeff_mat = sc_mat.multiply(sc_mat.transpose());
398 DecompositionSolver a_solver;
399 RealVector solved_amplitudes = null, amplitude_err = null;
400 boolean goodFit = true;
401 try {
402 CholeskyDecomposition a_cholesky = new CholeskyDecomposition(coeff_mat);
403 a_solver = a_cholesky.getSolver();
404 solved_amplitudes = a_solver.solve(a_vec);
405 amplitude_err = a_solver.solve(sc_mat.operate(var_vec));
406 if (solved_amplitudes.getSubVector(0, nFittedPulses).getMinValue() < 0) {
407 goodFit = false;
408 }
409 } catch (NonPositiveDefiniteMatrixException e) {
410 goodFit = false;
411 }
412
413 if (!goodFit) {
414 solved_amplitudes = new ArrayRealVector(nAmplitudes, 0.0);
415 amplitude_err = new ArrayRealVector(nAmplitudes, Double.POSITIVE_INFINITY);
416 }
417
418 double chisq = y_vec.subtract(sc_mat.preMultiply(solved_amplitudes)).getNorm();
419
420 for (int i = 0; i < nFittedPulses; i++) {
421 amplitudes[firstFittedPulse + i] = solved_amplitudes.getEntry(i);
422 amplitudeErrors[firstFittedPulse + i] = Math.sqrt(amplitude_err.getEntry(i));
423 }
424 if (fitPedestal) {
425 pedestal = solved_amplitudes.getEntry(nFittedPulses);
426 }
427 return chisq;
428 }
429
430
431
432
433
434
435
436
437
438
439 @Override
440 public double valueOf(double[] times) {
441 return doLinFit(times);
442 }
443 }