/** LMfit2D Levenberg Marquardt demonstrator; coordinates are microns * * Demonstrates fitting a 2D circular Gaussian to noisy data. * Five parameters: Xcenter, Ycenter, FWHM, Signal, Background. * X, Y, and FWHM are in microns. * Signal is in electrons total for the exposure. * Background is electrons per fiber for the exposure. * Image is an arbitrary grid of pixels. * Data come from a smooth model + random number generator. * Gaussian noise in each image pixel is sqrt(signal+background). * Computes best fit parameters using LM. * Computes their one-sigma errors by 3-point FD curvature. * * This one file contains several classes: * LMfitGauss contains only main(); creates a FitHost. * FitHost defines the fitting problem. * LMhost defines interface methods required by LM. * LM The Levenberg-Marquardt routine. * * M.Lampton UCB SSL (c) 1996; Java edition 2007, 2011, 2015 * see M.Lampton, 1997 Computers In Physics v.11 #10 110-115. */ public class LMfit2D { public static void main(String args[]) { new FitHost(); } } /** FitHost * Fits a five parameter Gaussian to pixellized noisy data * With or without subpixel averaging. * Sets up data with noise, and all needed arrays. * Triggers LM by instantiating an object of class LM * Offers four callback methods mandated by interface LMhost */ class FitHost implements LMhost { //--------constants--------------- protected final double RFIBER = 55.0; // microns protected final double READNOISE = 100.0; // rms electrons per fiber //----dark current is included within background----- //---List the pixel center locations, microns----- double pixXY[][] = {{-200.0, -200.0}, {-100.0, -200.0}, {-000.0, -200.0}, {+100.0, -200.0}, {+200.0, -200.0}, {-200.0, -100.0}, {-100.0, -100.0}, {-000.0, -100.0}, {+100.0, -100.0}, {+200.0, -100.0}, {-200.0, -000.0}, {-100.0, -000.0}, {-000.0, -000.0}, {+100.0, -000.0}, {+200.0, -000.0}, {-200.0, +100.0}, {-100.0, +100.0}, {-000.0, +100.0}, {+100.0, +100.0}, {+200.0, +100.0}, {-200.0, +200.0}, {-100.0, +200.0}, {-000.0, +200.0}, {+100.0, +200.0}, {+200.0, +200.0}}; int NPTS = pixXY.length; //---List the averaging points within one fiber----------------------- //---Use a square array for square pixels----------------------------- //---Here is a 19 point equal area circular array for round fibers---- //---These are normalized to a unit radius circular fiber------------- double aveXY[][] = {{ 0.000000, 0.000000}, { 0.346410, 0.200000}, { 0.000000, 0.400000}, {-0.346410, 0.200000}, {-0.346410, -0.200000}, {-0.000000, -0.400000}, { 0.346410, -0.200000}, { 0.800000, 0.000000}, { 0.692820, 0.400000}, { 0.400000, 0.692820}, { 0.000000, 0.800000}, {-0.400000, 0.692820}, {-0.692820, 0.400000}, {-0.800000, 0.000000}, {-0.692820, -0.400000}, {-0.400000, -0.692820}, {-0.000000, -0.800000}, { 0.400000, -0.692820}, { 0.692820, -0.400000}}; int NAVE = aveXY.length; //---Set up true Gaussian PSF a bit off center---------------------- //---Here, xc yc and FWHM are in microns------------------------------- //---signal is total star electrons in Texposure----------------- //---background is from sky and dark current, electrons/fiber-------- protected int NPARMS = 5; //----parms are...... xc, yc, FWHM, signal, bkg/pix. private double truep[] = { 10., 10., 120.0, 12000.0, 100.0}; // true parms private double parms[] = { 0.0, 0.0, 90.0, 25000.0, 0.0}; // start fit private double data[] = new double[NPTS]; // fake data list private double noise[] = new double[NPTS]; // model rms noise per fiber private double resid[] = new double[NPTS]; // current fit discrepancy private double jac[][] = new double[NPTS][NPARMS]; // jacobian partial derivatives private double sigma[] = new double[NPARMS]; // derived parameter errors public FitHost() { listParms("True Parms", truep); listParms("Start Parms", parms); putNoisyData(); listNoisyData(); // now invoke the Levenberg-Marquardt fitter.... LM myLM = new LM(this, NPARMS, NPTS); listParms("Best Fit Parms", parms); getSigmas(parms, sigma); listSigma("Parm errors", sigma); } //------------mathematical helpers for FitHost-------------- void listParms(String title, double p[]) { System.out.println(title + "----"); System.out.printf(" X= %.2f %n",p[0]); System.out.printf(" Y= %.2f %n",p[1]); System.out.printf(" F= %.2f %n",p[2]); System.out.printf(" S= %.2f %n",p[3]); System.out.printf(" B= %.2f %n",p[4]); } private void putNoiselessModel(double p[], double result[]) // This generates the noiseless fitting function for any model parameters p[]. // Here I integrate NAVE samples arranged within a circular fiber. { double dArea = Math.PI*RFIBER*RFIBER/NAVE; // square microns for (int i=0; i 15) return 0.0; return Math.exp(-arg)/(2*Math.PI*s*s); } private double gaussrand() // Returns a 1D gaussian variate, unit variance, zero mean { double sum = -6.0; for (int i=0; i<12; i++) sum += Math.random(); return sum; } private double chisq(double p[]) // Uses any given parameters vector; // Evaluates resid[] vector. // Returns chi squared = weighted-sum-of-squares. { double model[] = new double[NPTS]; putNoiselessModel(p, model); double sumsq = 0.0; for (int i=0; i-LMTOL) || (lambda>LAMMAX); return done; } private double gaussj( double[][] a, int N ) // Inverts the double array a[N][N] by Gauss-Jordan method // M.Lampton UCB SSL (c)2003, 2005 { double det = 1.0, big, save; int i,j,k,L; int[] ik = new int[100]; int[] jk = new int[100]; for (k=0; kk) for (j=0; jk) for (i=0; ik) for (i=0; ik) for (j=0; j