View Javadoc
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  //===> import org.hps.conditions.deprecated.HPSSVTCalibrationConstants.ChannelConstants;
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   * Fast fitter; currently only fits single hits. Uses Tp from ChannelConstants;
28   * fits values and errors for T0 and amplitude.
29   *
30   * @author Sho Uemura
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      //===> private ChannelConstants channelConstants;
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      //===> public Collection<ShapeFitParameters> fitShape(RawTrackerHit rth, ChannelConstants constants) {
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          //===> return this.fitShape(rth.getADCValues(), constants);
90      }
91  
92      public Collection<ShapeFitParameters> fitShape(short[] samples) {
93          //===> public Collection<ShapeFitParameters> fitShape(short[] samples, ChannelConstants constants) {
94          // channelConstants = constants;
95          double[] signal = new double[HPSSVTConstants.TOTAL_NUMBER_OF_SAMPLES];
96  
97          for (int i = 0; i < samples.length; i++) {
98              //===> signal[i] = samples[i] - constants.getPedestal();
99              signal[i] = samples[i] - sensor.getPedestal(channel, i);
100             //===> sigma[i] = constants.getNoise();
101             sigma[i] = sensor.getNoise(channel, i);
102         }
103 
104 //        if (signal[0]>300.0) {
105 //            debug = true;
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 //        if (!min.isValid() && nPulses == 2) {
122 //            System.out.format("bad fit to %d pulses, chisq %f\n", nPulses, min.fval());
123 //            if (!debug) {
124 //                debug = true;
125 //                doRecursiveFit(signal);
126 //                debug = false;
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 //            MinosError t0err = null;
148 //            if (min.isValid() && min.edm() > 0) {
149 //                MnMinos minos = null;
150 //
151 //                try {
152 //                    minos = new MnMinos(this, min);
153 //                    t0err = minos.minos(0);
154 //                } catch (RuntimeException e) {
155 //                    if (debug) {
156 //                        System.out.println(e);
157 //                    }
158 //                }
159 //            }
160 //            if (t0err != null && t0err.isValid()) {
161 //                if (debug) {
162 //                    System.out.format("fitter error %f, minos lower %f, upper %f\n", min.userState().error(i), t0err.lower(), t0err.upper());
163 //                }
164 //                fit.setT0Err((t0err.lower() + t0err.upper()) / 2);
165 //            } else {
166 //                if (debug) {
167 //                    System.out.format("fitter error %f\n", min.userState().error(i));
168 //                }
169 //            }
170 //        System.out.println(fit);
171             fits.add(fit);
172         }
173 //        debug = false;
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                 //use signal as fit input
192                 System.arraycopy(samples, 0, fitData, 0, samples.length);
193                 //use first $split samples
194                 firstUsedSample = 0;
195                 nUsedSamples = split;
196                 //fit only the first pulse
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                 //subtract first pulse from fit input
208                 for (int i = 0; i < samples.length; i++) {
209                     //===> fitData[i] -= amplitudes[firstFittedPulse] * getAmplitude(HPSSVTConstants.SAMPLING_INTERVAL * i - frontFit.userState().value(0), channelConstants);
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                 //use all samples
224                 firstUsedSample = 0;
225                 nUsedSamples = y.length;
226                 //fit the rest of the pulses
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                 //use full signal as fit input
243                 System.arraycopy(samples, 0, y, 0, samples.length);
244                 //still using all samples
245                 //fit all pulses
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 //            double newchisq = evaluateMinimum(bestFit);
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 //                System.out.println("made guess " + guess);
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             /*double sample_time = HPSSVTConstants.SAMPLING_INTERVAL * (firstUsedSample + j);
372             for (int i = 0; i < nFittedPulses; i++) {
373                 //===> sc_mat.setEntry(i, j, getAmplitude(HPSSVTConstants.SAMPLING_INTERVAL * (firstUsedSample + j) - times[i], channelConstants) / sigma[firstUsedSample + j]);
374                 sc_mat.setEntry(i, j, shape.getAmplitudePeakNorm(sample_time - times[i]) / sigma_j);
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         // amplitudez is spelled with a zed for a reazon.  Changing the spelling to use an "s" will break the code.  believe me I tried.      
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                 //double err = amplitudes[j]/sigma[firstUsedSample+j]-sc_mat.getEntry(i, j);
391                 //if(Math.abs(err) > 1e-10)
392                     //System.out.println(amplitudes[j]/sigma[firstUsedSample + j] + " " + err);
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 //    //===> private static double getAmplitude(double time, ChannelConstants channelConstants) {
431 //    private static double getAmplitude(double time, int channel, HpsSiSensor sensor) {
432 //        if (time < 0) {
433 //            return 0;
434 //        }
435 //        double tp = sensor.getShapeFitParameters(channel)[HpsSiSensor.TP_INDEX];
436 //        //===> return (time / channelConstants.getTp()) * Math.exp(1 - time / channelConstants.getTp());
437 //        return (time / tp) * Math.exp(1 - time / tp);
438 //    }
439     @Override
440     public double valueOf(double[] times) {
441         return doLinFit(times);
442     }
443 }