Ejemplo n.º 1
0
def powerfit(xax, data, err=None, alphaguess=-2.0, scaleguess=1.0, quiet=True):
    """
    Fit a power law (a line in log-space) to data as a function of x
    differs from 'plfit' because plfit fits a power law distribution, 
    this code simply fits a power law
    """

    logdata = np.log10(data)
    if err is None: err = np.ones(data.shape, dtype='float')

    def mpfitfun(data, err):
        def f(p, fjac=None):
            return [
                0,
                np.ravel(
                    ((np.log10(p[0]) + np.log10(xax) * p[1]) - data) / err)
            ]

        return f

    mp = mpfit.mpfit(mpfitfun(logdata, err),
                     xall=[scaleguess, alphaguess],
                     quiet=quiet)
    fitp = mp.params

    return fitp, mp
Ejemplo n.º 2
0
    def fit_blackbody(xdata, flux, guesses=(0,0), err=None,
            blackbody_function=blackbody, quiet=True, **kwargs):
        """
        Parameters
        ----------
        xdata : array
            Array of the X-values (frequency, wavelength) of the data
        flux : array
            The fluxes corresponding to the xdata values
        guesses : (Temperature,Scale) or (Temperature,Beta,Scale)
            The input guesses.  3 parameters are used for greybody 
            fitting, two for temperature fitting.
        blackbody_function: function
            Must take x-axis (e.g. frequency), temperature, scale, and then
            optionally beta args 
        quiet : bool
            quiet flag passed to mpfit

        Returns
        -------
        mp : mpfit structure
            An mpfit structure.  Access parameters and errors via
            `mp.params` and `mp.perror`.  The covariance matrix
            is in mp.covar.

        Examples
        --------
        >>> wavelength = array([20,70,160,250,350,500,850,1100])
        >>> flux = modified_blackbody_wavelength(wavelength, 15, beta=1.75,
                logN=22, wavelength_units='microns', normalize=False,
                logscale=16)
        >>> err = 0.1 * flux
        >>> np.random.seed(0)
        >>> flux += np.random.randn(len(wavelength)) * err
        >>> tguess, bguess, nguess = 20.,2.,21.5
        >>> mp = fit_blackbody(wavelength, flux, err=err,
                 blackbody_function=modified_blackbody_wavelength,
                 logscale=16, guesses=(tguess, bguess, nguess),
                 wavelength_units='microns')
        >>> print mp.params 
        [ 14.99095224   1.78620237  22.05271119]
        >>> # T~14.9 K, beta ~1.79, column ~10^22
        """

        def mpfitfun(x,y,err):
            if err is None:
                def f(p,fjac=None): return [0,(y-blackbody_function(x, *p,
                    normalize=False, **kwargs))]
            else:
                def f(p,fjac=None): return [0,(y-blackbody_function(x, *p,
                    normalize=False, **kwargs))/err]
            return f

        err = err if err is not None else flux*0.0 + 1.0

        mp = mpfit.mpfit(mpfitfun(xdata,flux,err), guesses, quiet=quiet)

        return mp
Ejemplo n.º 3
0
def mpfitter(xdata, ydata, params=None, error=None, model=gaussian, quiet=True, **kwargs):
    """
    Find the least-squares fit using mpfit
    """

    errfunc = error_function_generator(xdata,ydata,error=error,model=model,mpfit=True)

    mp = mpfit(errfunc, params, quiet=quiet, **kwargs)

    return mp.params,mp.perror
Ejemplo n.º 4
0
def brokenpowerfit(xax, data, err=None, alphaguess1=0.0, alphaguess2=-2.0, scaleguess=1.0,
        breakpoint=None, quiet=True):
    """
    Fit a broken power law (a line in log-space) to data as a function of x
    differs from 'plfit' because plfit fits a power law distribution, 
    this code simply fits a power law

    This is a lot more intricate than the simple power law fit, since it involves
    fitting two power laws with different slopes

    Parameters:
    p[0] - scale
    p[1] - breakpoint
    p[2] - power 1 (xax < breakpoint)
    p[3] - power 2 (xax >= breakpoint)

    There are 5 parameters (NOT 4) returned because there are two scales that are *NOT*
    independent

    returns: scale1,scale2,breakpoint,alpha1,alpha2

    """
    
    logdata = np.log10(data)
    if err is None: err = np.ones(data.shape,dtype='float')

    def brokenpowerlaw(p):
        lowerhalf = (np.log10(p[0]) + np.log10(xax)*p[2]) * (xax < p[1])
        # find the location at which both functions must be equal
        scale2loc = np.argmin(np.abs(xax - p[1]))
        scale2 = np.log10(xax[scale2loc])*(p[2] - p[3]) + np.log10(p[0])
        upperhalf = (scale2 + np.log10(xax)*p[3]) * (xax >= p[1])
        # DEBUG print "scale1: %15g   scale2: %15g  xaxind: %5i  xaxval: %15g  lower: %15g upper: %15g" % (p[0],scale2,scale2loc,np.log10(xax[scale2loc]),lowerhalf[scale2loc-1],upperhalf[scale2loc])
        return lowerhalf+upperhalf


    def mpfitfun(data,err):
        def f(p,fjac=None): return [0,np.ravel((brokenpowerlaw(p)-data)/err)]
        return f

    if breakpoint is None: breakpoint = np.median(xax)

    parinfo = [{}, {'mpminstep':xax.min(),'mpmaxstep':xax.max(),'step':xax.min()}, {}, {}]
        
    mp = mpfit.mpfit(mpfitfun(logdata,err),xall=[scaleguess,breakpoint,alphaguess1,alphaguess2],quiet=quiet,parinfo=parinfo)
    fitp = mp.params

    scale2loc = np.argmin(np.abs(xax - fitp[1]))
    scale2 = 10**( np.log10(xax[scale2loc])*(fitp[2] - fitp[3]) + np.log10(fitp[0]) )
    fitp = np.array( [fitp[0],scale2] + fitp[1:].tolist() )

    return fitp,mp
Ejemplo n.º 5
0
def emtau(freq,flux,err=None,EMguess=1e7,Te=8500,normfac=5e-6,quiet=1):
    """
    Returns emission measure & optical depth given 
    radio continuum data points at frequency freq with flux
    density flux.

    return bestEM,nu(tau=1),chi^2
    """
    mp = mpfit(mpfitfun(freq,flux,err),xall=[EMguess,normfac],quiet=quiet)
    mpp = mp.params
    mpperr = mp.perror
    chi2 = mp.fnorm
    bestEM = mpp[0]
    normfac = mpp[1]
    nu_tau = ( Te**1.35 / bestEM / 8.235e-2 )**(-1/2.1)

    return bestEM,nu_tau,normfac,chi2
Ejemplo n.º 6
0
def powerfit(xax,data,err=None,alphaguess=-2.0,scaleguess=1.0,quiet=True):
    """
    Fit a power law (a line in log-space) to data as a function of x
    differs from 'plfit' because plfit fits a power law distribution, 
    this code simply fits a power law
    """
    
    logdata = np.log10(data)
    if err is None: err = np.ones(data.shape,dtype='float')

    def mpfitfun(data,err):
        def f(p,fjac=None): return [0,np.ravel(((np.log10(p[0])+np.log10(xax)*p[1])-data)/err)]
        return f
        
    mp = mpfit.mpfit(mpfitfun(logdata,err),xall=[scaleguess,alphaguess],quiet=quiet)
    fitp = mp.params

    return fitp,mp
Ejemplo n.º 7
0
def mpfitter(xdata,
             ydata,
             params=None,
             error=None,
             model=gaussian,
             quiet=True,
             **kwargs):
    """
    Find the least-squares fit using mpfit
    """

    errfunc = error_function_generator(xdata,
                                       ydata,
                                       error=error,
                                       model=model,
                                       mpfit=True)

    mp = mpfit(errfunc, params, quiet=quiet, **kwargs)

    return mp.params, mp.perror
Ejemplo n.º 8
0
def fitspec(specsegments,
            aperture=None,
            modelspec=modelspec,
            outpars=False,
            vlsrcorr=0,
            extinction=False,
            parinfo=modelpars(),
            quiet=1,
            **kwargs):
    """ fit a model spectrum 
    The model is defined internal to fitspec so that parameters can
    be fixed based on input parameters """

    specsegments = copy(specsegments)  # copy.deepcopy(specsegments)
    parinfo = copy(parinfo)  # copy.deepcopy(parinfo)

    kwargs = {'extinction': extinction}
    if extinction:
        parinfo = modelpars(extinction=extinction)

    def fitfun(x, y, err):
        return lambda (p): (y - modelspec(x, *p, **kwargs)) / err

    def mpfitfun(x, y, err):
        def f(p, fjac=None):
            return [0, (y - modelspec(x, *p, **kwargs)) / err]

        return f

    if aperture is not None:
        miny, maxy = aperture

        fitdata = asarray([
            ss['data'][miny:maxy, :].sum(axis=0) for ss in specsegments
        ]).ravel()
        fiterr = asarray([
            ss['err'][:] * sqrt(maxy - miny) for ss in specsegments
        ]).ravel()  # since it is a summed aperture, add in quadrature
    else:
        fitdata = asarray([ss['data'] for ss in specsegments]).ravel()
        fiterr = asarray([ss['err'] for ss in specsegments]).ravel()

    fitwl = asarray([ss['wavelength'] for ss in specsegments]).ravel()

    try:
        parinfo = parinfo.tolist()
        print "Parinfo was an array.  No idea why, it was never set to one.  Ever."
    except:
        pass

    mp = mpfit(mpfitfun(fitwl, fitdata, fiterr), parinfo=parinfo, quiet=quiet)
    mpp = mp.params
    mpperr = mp.perror

    for i, p in enumerate(mpp):
        parinfo[i]['value'] = p
        if parinfo[i]['parname'] == 'SHIFT':
            print parinfo[i]['parname'], p + vlsrcorr, " +/- ", mpperr[i]
        else:
            print parinfo[i]['parname'], p, " +/- ", mpperr[i]


#    print "Temperature: ",mpp[0]," Shift: ",mpp[3],mpp[3]*mean(fitwl)/3e5," Width: ",mpp[2],mpp[2]*mean(fitwl)/3e5,"   Ortho/Para: ",mpp[4]
#    print "ERRORS: Temperature: ",mpperr[0]," Shift: ",mpperr[3],mpperr[3]*mean(fitwl)/3e5," Width: ",mpperr[2],mpperr[2]*mean(fitwl)/3e5,"   Ortho/Para: ",mpperr[4]
    print "Chi2: ", mp.fnorm, " Reduced Chi2: ", mp.fnorm / len(
        fitdata), " DOF:", len(fitdata) - len(mpp)

    apfitd = {
        'params': mpp,
        'parerr': mpperr,
        'parinfo': parinfo,
        'wl': fitwl,
        'data': fitdata,
        'model': modelspec(fitwl, *mpp),
        'err': fiterr
    }

    if aperture is not None:
        for ss in specsegments:
            ss['data'] = ss['data'][miny:maxy, :].sum(axis=0)
    for ss in specsegments:
        ss['model'] = modelspec(ss['wavelength'], *mpp)

    if outpars: return specsegments, apfitd, parinfo
    else: return specsegments, apfitd
Ejemplo n.º 9
0
def multigaussfit(xax, data, ngauss=1, err=None, params=[1,0,1],
        fixed=[False,False,False], limitedmin=[False,False,True],
        limitedmax=[False,False,False], minpars=[0,0,0], maxpars=[0,0,0],
        quiet=True, shh=True, veryverbose=False):
    """
    An improvement on onedgaussfit.  Lets you fit multiple gaussians.

    Inputs:
       xax - x axis
       data - y axis
       ngauss - How many gaussians to fit?  Default 1 (this could supersede onedgaussfit)
       err - error corresponding to data

     These parameters need to have length = 3*ngauss.  If ngauss > 1 and length = 3, they will
     be replicated ngauss times, otherwise they will be reset to defaults:
       params - Fit parameters: [amplitude, offset, width] * ngauss
              If len(params) % 3 == 0, ngauss will be set to len(params) / 3
       fixed - Is parameter fixed?
       limitedmin/minpars - set lower limits on each parameter (default: width>0)
       limitedmax/maxpars - set upper limits on each parameter

       quiet - should MPFIT output each iteration?
       shh - output final parameters?

    Returns:
       Fit parameters
       Model
       Fit errors
       chi2
    """

    if len(params) != ngauss and (len(params) / 3) > ngauss:
        ngauss = len(params) / 3 

    if isinstance(params,numpy.ndarray): params=params.tolist()

    # make sure all various things are the right length; if they're not, fix them using the defaults
    for parlist in (params,fixed,limitedmin,limitedmax,minpars,maxpars):
        if len(parlist) != 3*ngauss:
            # if you leave the defaults, or enter something that can be multiplied by 3 to get to the
            # right number of gaussians, it will just replicate
            if len(parlist) == 3: 
                parlist *= ngauss 
            elif parlist==params:
                parlist[:] = [1,0,1] * ngauss
            elif parlist==fixed or parlist==limitedmax:
                parlist[:] = [False,False,False] * ngauss
            elif parlist==limitedmin:
                parlist[:] = [False,False,True] * ngauss
            elif parlist==minpars or parlist==maxpars:
                parlist[:] = [0,0,0] * ngauss

    def mpfitfun(x,y,err):
        if err is None:
            def f(p,fjac=None): return [0,(y-n_gaussian(pars=p)(x))]
        else:
            def f(p,fjac=None): return [0,(y-n_gaussian(pars=p)(x))/err]
        return f

    if xax == None:
        xax = numpy.arange(len(data))

    parnames = {0:"AMPLITUDE",1:"SHIFT",2:"WIDTH"}

    parinfo = [ {'n':ii, 'value':params[ii],
        'limits':[minpars[ii],maxpars[ii]],
        'limited':[limitedmin[ii],limitedmax[ii]], 'fixed':fixed[ii],
        'parname':parnames[ii%3]+str(ii%3), 'error':ii} 
        for ii in xrange(len(params)) ]

    if veryverbose:
        print "GUESSES: "
        print "\n".join(["%s: %s" % (p['parname'],p['value']) for p in parinfo])

    mp = mpfit(mpfitfun(xax,data,err),parinfo=parinfo,quiet=quiet)
    mpp = mp.params
    mpperr = mp.perror
    chi2 = mp.fnorm

    if mp.status == 0:
        raise Exception(mp.errmsg)

    if not shh:
        print "Final fit values: "
        for i,p in enumerate(mpp):
            parinfo[i]['value'] = p
            print parinfo[i]['parname'],p," +/- ",mpperr[i]
        print "Chi2: ",mp.fnorm," Reduced Chi2: ",mp.fnorm/len(data)," DOF:",len(data)-len(mpp)

    return mpp,n_gaussian(pars=mpp)(xax),mpperr,chi2
Ejemplo n.º 10
0
def onedgaussfit(xax, data, err=None,
        params=[0,1,0,1],fixed=[False,False,False,False],
        limitedmin=[False,False,False,True],
        limitedmax=[False,False,False,False], minpars=[0,0,0,0],
        maxpars=[0,0,0,0], quiet=True, shh=True,
        veryverbose=False,
        vheight=True, negamp=False,
        usemoments=False):
    """
    Inputs:
       xax - x axis
       data - y axis
       err - error corresponding to data

       params - Fit parameters: Height of background, Amplitude, Shift, Width
       fixed - Is parameter fixed?
       limitedmin/minpars - set lower limits on each parameter (default: width>0)
       limitedmax/maxpars - set upper limits on each parameter
       quiet - should MPFIT output each iteration?
       shh - output final parameters?
       usemoments - replace default parameters with moments

    Returns:
       Fit parameters
       Model
       Fit errors
       chi2
    """

    def mpfitfun(x,y,err):
        if err is None:
            def f(p,fjac=None): return [0,(y-onedgaussian(x,*p))]
        else:
            def f(p,fjac=None): return [0,(y-onedgaussian(x,*p))/err]
        return f

    if xax == None:
        xax = numpy.arange(len(data))

    if vheight is False: 
        height = params[0]
        fixed[0] = True
    if usemoments:
        params = onedmoments(xax,data,vheight=vheight,negamp=negamp, veryverbose=veryverbose)
        if vheight is False: params = [height]+params
        if veryverbose: print "OneD moments: h: %g  a: %g  c: %g  w: %g" % tuple(params)

    parinfo = [ {'n':0,'value':params[0],'limits':[minpars[0],maxpars[0]],'limited':[limitedmin[0],limitedmax[0]],'fixed':fixed[0],'parname':"HEIGHT",'error':0} ,
                {'n':1,'value':params[1],'limits':[minpars[1],maxpars[1]],'limited':[limitedmin[1],limitedmax[1]],'fixed':fixed[1],'parname':"AMPLITUDE",'error':0},
                {'n':2,'value':params[2],'limits':[minpars[2],maxpars[2]],'limited':[limitedmin[2],limitedmax[2]],'fixed':fixed[2],'parname':"SHIFT",'error':0},
                {'n':3,'value':params[3],'limits':[minpars[3],maxpars[3]],'limited':[limitedmin[3],limitedmax[3]],'fixed':fixed[3],'parname':"WIDTH",'error':0}]

    mp = mpfit(mpfitfun(xax,data,err),parinfo=parinfo,quiet=quiet)
    mpp = mp.params
    mpperr = mp.perror
    chi2 = mp.fnorm

    if mp.status == 0:
        raise Exception(mp.errmsg)

    if (not shh) or veryverbose:
        print "Fit status: ",mp.status
        for i,p in enumerate(mpp):
            parinfo[i]['value'] = p
            print parinfo[i]['parname'],p," +/- ",mpperr[i]
        print "Chi2: ",mp.fnorm," Reduced Chi2: ",mp.fnorm/len(data)," DOF:",len(data)-len(mpp)

    return mpp,onedgaussian(xax,*mpp),mpperr,chi2
Ejemplo n.º 11
0
def gaussfit(data,err=None,params=(),autoderiv=True,return_all=False,circle=False,
        fixed=numpy.repeat(False,7),limitedmin=[False,False,False,False,True,True,True],
        limitedmax=[False,False,False,False,False,False,True],
        usemoment=numpy.array([],dtype='bool'),
        minpars=numpy.repeat(0,7),maxpars=[0,0,0,0,0,0,360],
        rotate=1,vheight=1,quiet=True,returnmp=False,
        returnfitimage=False,**kwargs):
    """
    Gaussian fitter with the ability to fit a variety of different forms of
    2-dimensional gaussian.
    
    Input Parameters:
        data - 2-dimensional data array
        err=None - error array with same size as data array
        params=[] - initial input parameters for Gaussian function.
            (height, amplitude, x, y, width_x, width_y, rota)
            if not input, these will be determined from the moments of the system, 
            assuming no rotation
        autoderiv=1 - use the autoderiv provided in the lmder.f function (the
            alternative is to us an analytic derivative with lmdif.f: this method
            is less robust)
        return_all=0 - Default is to return only the Gaussian parameters.  
                   1 - fit params, fit error
        returnfitimage - returns (best fit params,best fit image)
        returnmp - returns the full mpfit struct
        circle=0 - default is an elliptical gaussian (different x, y widths),
            but can reduce the input by one parameter if it's a circular gaussian
        rotate=1 - default allows rotation of the gaussian ellipse.  Can remove
            last parameter by setting rotate=0.  numpy.expects angle in DEGREES
        vheight=1 - default allows a variable height-above-zero, i.e. an
            additive constant for the Gaussian function.  Can remove first
            parameter by setting this to 0
        usemoment - can choose which parameters to use a moment estimation for.
            Other parameters will be taken from params.  Needs to be a boolean
            array.

    Output:
        Default output is a set of Gaussian parameters with the same shape as
            the input parameters

        If returnfitimage=True returns a numpy array of a guassian 
            contructed using the best fit parameters.

        If returnmp=True returns a `mpfit` object. This object contains 
            a `covar` attribute which is the 7x7 covariance array 
            generated by the pmfit class in the `mpfit_custom.py` 
            module. It contains a `param` attribute that contains a 
            list of the best fit parameters in the same order as the 
            optional input parameter `params`.  

        Warning: Does NOT necessarily output a rotation angle between 0 and 360 degrees.
    """
    usemoment=numpy.array(usemoment,dtype='bool')
    params=numpy.array(params,dtype='float')
    if usemoment.any() and len(params)==len(usemoment):
        moment = numpy.array(moments(data,circle,rotate,vheight,**kwargs),dtype='float')
        params[usemoment] = moment[usemoment]
    elif params == [] or len(params)==0:
        params = (moments(data,circle,rotate,vheight,**kwargs))
    if vheight==0:
        vheight=1
        params = numpy.concatenate([[0],params])
        fixed[0] = 1


    # mpfit will fail if it is given a start parameter outside the allowed range:
    for i in xrange(len(params)): 
        if params[i] > maxpars[i] and limitedmax[i]: params[i] = maxpars[i]
        if params[i] < minpars[i] and limitedmin[i]: params[i] = minpars[i]

    if err is None:
        errorfunction = lambda p: numpy.ravel((twodgaussian(p,circle,rotate,vheight)\
                (*numpy.indices(data.shape)) - data))
    else:
        errorfunction = lambda p: numpy.ravel((twodgaussian(p,circle,rotate,vheight)\
                (*numpy.indices(data.shape)) - data)/err)
    def mpfitfun(data,err):
        if err is None:
            def f(p,fjac=None): return [0,numpy.ravel(data-twodgaussian(p,circle,rotate,vheight)\
                    (*numpy.indices(data.shape)))]
        else:
            def f(p,fjac=None): return [0,numpy.ravel((data-twodgaussian(p,circle,rotate,vheight)\
                    (*numpy.indices(data.shape)))/err)]
        return f

                    
    parinfo = [ 
                {'n':1,'value':params[1],'limits':[minpars[1],maxpars[1]],'limited':[limitedmin[1],limitedmax[1]],'fixed':fixed[1],'parname':"AMPLITUDE",'error':0},
                {'n':2,'value':params[2],'limits':[minpars[2],maxpars[2]],'limited':[limitedmin[2],limitedmax[2]],'fixed':fixed[2],'parname':"XSHIFT",'error':0},
                {'n':3,'value':params[3],'limits':[minpars[3],maxpars[3]],'limited':[limitedmin[3],limitedmax[3]],'fixed':fixed[3],'parname':"YSHIFT",'error':0},
                {'n':4,'value':params[4],'limits':[minpars[4],maxpars[4]],'limited':[limitedmin[4],limitedmax[4]],'fixed':fixed[4],'parname':"XWIDTH",'error':0} ]
    if vheight == 1:
        parinfo.insert(0,{'n':0,'value':params[0],'limits':[minpars[0],maxpars[0]],'limited':[limitedmin[0],limitedmax[0]],'fixed':fixed[0],'parname':"HEIGHT",'error':0})
    if circle == 0:
        parinfo.append({'n':5,'value':params[5],'limits':[minpars[5],maxpars[5]],'limited':[limitedmin[5],limitedmax[5]],'fixed':fixed[5],'parname':"YWIDTH",'error':0})
        if rotate == 1:
            parinfo.append({'n':6,'value':params[6],'limits':[minpars[6],maxpars[6]],'limited':[limitedmin[6],limitedmax[6]],'fixed':fixed[6],'parname':"ROTATION",'error':0})

    if autoderiv == 0:
        # the analytic derivative, while not terribly difficult, is less
        # efficient and useful.  I only bothered putting it here because I was
        # instructed to do so for a class project - please ask if you would
        # like this feature implemented
        raise ValueError("I'm sorry, I haven't implemented this feature yet.")
    else:
#        p, cov, infodict, errmsg, success = optimize.leastsq(errorfunction,\
#                params, full_output=1)
        mp = mpfit(mpfitfun(data,err),parinfo=parinfo,quiet=quiet)


    if returnmp:
        returns = (mp)
    elif return_all == 0:
        returns = mp.params
    elif return_all == 1:
        returns = mp.params,mp.perror
    if returnfitimage:
        fitimage = twodgaussian(mp.params,circle,rotate,vheight)(*numpy.indices(data.shape))
        returns = (returns,fitimage)
    return returns
Ejemplo n.º 12
0
def psffit(data,
           err=None,
           params=[],
           autoderiv=True,
           return_all=False,
           circle=True,
           fixed=numpy.repeat(False, 7),
           limitedmin=[False, False, False, False, True, True, True],
           limitedmax=[False, False, False, False, False, False, True],
           usemoment=numpy.array([], dtype='bool'),
           minpars=numpy.repeat(0, 7),
           maxpars=[0, 0, 0, 0, 0, 0, 360],
           rotate=0,
           vheight=1,
           quiet=True,
           returnmp=False,
           returnfitimage=False,
           psffunction=airy,
           extra_pars=None,
           return_parinfo=False,
           **kwargs):
    """
    PSF fitter with the ability to fit a variety of different forms of
    2-dimensional gaussian OR an Airy.
    This code is mostly directly copied from gaussfitter.py and presents 
    yet another argument for me turning this into a class...
    
    Input Parameters:
        data - 2-dimensional data array
        err=None - error array with same size as data array
        params=[] - initial input parameters for Gaussian function.
            (height, amplitude, x, y, width_x, width_y, rota)
            if not input, these will be determined from the moments of the system, 
            assuming no rotation
        autoderiv=1 - use the autoderiv provided in the lmder.f function (the
            alternative is to us an analytic derivative with lmdif.f: this method
            is less robust)
        return_all=0 - Default is to return only the Gaussian parameters.  
                   1 - fit params, fit error
        returnfitimage - returns (best fit params,best fit image)
        returnmp - returns the full mpfit struct
        circle=0 - default is an elliptical gaussian (different x, y widths),
            but can reduce the input by one parameter if it's a circular gaussian
        rotate=1 - default allows rotation of the gaussian ellipse.  Can remove
            last parameter by setting rotate=0.  numpy.expects angle in DEGREES
        vheight=1 - default allows a variable height-above-zero, i.e. an
            additive constant for the Gaussian function.  Can remove first
            parameter by setting this to 0
        usemoment - can choose which parameters to use a moment estimation for.
            Other parameters will be taken from params.  Needs to be a boolean
            array.
        extra_pars - If your psffunction requires extra parameters, pass their
            parinfo dictionaries through this variable

    Output:
        Default output is a set of Gaussian parameters with the same shape as
            the input parameters

        Can also output the covariance matrix, 'infodict' that contains a lot
            more detail about the fit (see scipy.optimize.leastsq), and a message
            from leastsq telling what the exit status of the fitting routine was

        Warning: Does NOT necessarily output a rotation angle between 0 and 360 degrees.
    """
    usemoment = numpy.array(usemoment, dtype='bool')
    params = numpy.array(params, dtype='float')
    if usemoment.any() and len(params) == len(usemoment):
        moment = numpy.array(moments(data, circle, rotate, vheight, **kwargs),
                             dtype='float')
        params[usemoment] = moment[usemoment]
    elif params == [] or len(params) == 0:
        params = (moments(data, circle, rotate, vheight, **kwargs))
    if vheight == 0:
        vheight = 1
        params = numpy.concatenate([[0], params])
        fixed[0] = 1

    # mpfit will fail if it is given a start parameter outside the allowed range:
    for i in xrange(len(params)):
        if params[i] > maxpars[i] and limitedmax[i]: params[i] = maxpars[i]
        if params[i] < minpars[i] and limitedmin[i]: params[i] = minpars[i]

    if err is None:
        errorfunction = lambda p: numpy.ravel((psffunction(p,circle,rotate,vheight)\
                (*numpy.indices(data.shape)) - data))
    else:
        errorfunction = lambda p: numpy.ravel((psffunction(p,circle,rotate,vheight)\
                (*numpy.indices(data.shape)) - data)/err)

    def mpfitfun(data, err):
        if err is None:

            def f(p, fjac=None):                return [0,numpy.ravel(data-psffunction(p,circle,rotate,vheight)\
    (*numpy.indices(data.shape)))]
        else:

            def f(p, fjac=None):                return [0,numpy.ravel((data-psffunction(p,circle,rotate,vheight)\
    (*numpy.indices(data.shape)))/err)]

        return f

    parinfo = []
    if vheight:
        parinfo.insert(
            0, {
                'n': 0,
                'value': params[0],
                'limits': [minpars[0], maxpars[0]],
                'limited': [limitedmin[0], limitedmax[0]],
                'fixed': fixed[0],
                'parname': "HEIGHT",
                'error': 0
            })
        ind = 1
    else:
        ind = 0

    parinfo.append({
        'n': 0 + ind,
        'value': params[0 + ind],
        'limits': [minpars[0 + ind], maxpars[0 + ind]],
        'limited': [limitedmin[0 + ind], limitedmax[0 + ind]],
        'fixed': fixed[0 + ind],
        'parname': "AMPLITUDE",
        'error': 0
    })
    parinfo.append({
        'n': 1 + ind,
        'value': params[1 + ind],
        'limits': [minpars[1 + ind], maxpars[1 + ind]],
        'limited': [limitedmin[1 + ind], limitedmax[1 + ind]],
        'fixed': fixed[1 + ind],
        'parname': "XSHIFT",
        'error': 0
    })
    parinfo.append({
        'n': 2 + ind,
        'value': params[2 + ind],
        'limits': [minpars[2 + ind], maxpars[2 + ind]],
        'limited': [limitedmin[2 + ind], limitedmax[2 + ind]],
        'fixed': fixed[2 + ind],
        'parname': "YSHIFT",
        'error': 0
    })
    parinfo.append({
        'n': 3 + ind,
        'value': params[3 + ind],
        'limits': [minpars[3 + ind], maxpars[3 + ind]],
        'limited': [limitedmin[3 + ind], limitedmax[3 + ind]],
        'fixed': fixed[3 + ind],
        'parname': "XWIDTH",
        'error': 0
    })

    if not (circle):
        parinfo.append({
            'n': 4 + ind,
            'value': params[4 + ind],
            'limits': [minpars[4 + ind], maxpars[4 + ind]],
            'limited': [limitedmin[4 + ind], limitedmax[4 + ind]],
            'fixed': fixed[4 + ind],
            'parname': "YWIDTH",
            'error': 0
        })
        if rotate:
            parinfo.append({
                'n':
                5 + ind,
                'value':
                params[5 + ind],
                'limits': [minpars[5 + ind], maxpars[5 + ind]],
                'limited': [limitedmin[5 + ind], limitedmax[5 + ind]],
                'fixed':
                fixed[5 + ind],
                'parname':
                "ROTATION",
                'error':
                0
            })

    if extra_pars:
        for P in extra_pars:
            parinfo.append(P)

    if autoderiv is False:
        # the analytic derivative, while not terribly difficult, is less
        # efficient and useful.  I only bothered putting it here because I was
        # instructed to do so for a class project - please ask if you would
        # like this feature implemented
        raise NotImplementedError(
            "I'm sorry, I haven't implemented this feature yet.")
    else:
        #        p, cov, infodict, errmsg, success = optimize.leastsq(errorfunction,\
        #                params, full_output=1)
        mp = mpfit(mpfitfun(data, err), parinfo=parinfo, quiet=quiet)

    if returnmp:
        returns = (mp)
    elif return_parinfo:
        returns = (parinfo)
    elif return_all is False:
        returns = mp.params
    elif return_all:
        returns = mp.params, mp.perror
    if returnfitimage:
        fitimage = psffunction(mp.params, circle, rotate,
                               vheight)(*numpy.indices(data.shape))
        returns = (returns, fitimage)
    return returns
Ejemplo n.º 13
0
# p[5]:Omega_bh2
# p[6]:Omega_m
# p[7]:Omega_r
# p[8]:Omega_k
# p[9]... other Cosmological parameters

# initial conditions

print 'Prepare and reading data ...'
p0 = np.array([0.141, 3.099, -19.10, -0.07, 68.34, 0.0221, 0.305, ogh2, 0],
              dtype='float64')
n = len(p0)

parbase = {'value': 0., 'fixed': 0, 'limited': [0, 0], 'limits': [0., 0.]}
parinfo = []

for i in range(n):
    parinfo.append(copy.deepcopy(parbase))
for i in range(n):
    parinfo[i]['value'] = p0[i]

m = mpfit(residual, p0, parinfo=parinfo)

if (m.status <= 0):
    print 'error message = ', m.errmsg

chisq = (residual(m.params)[1]**2).sum()

print m.params
print m.perror
Ejemplo n.º 14
0
Archivo: h2fit.py Proyecto: Fade89/agpy
def fitspec(specsegments,aperture=None,
        modelspec=modelspec,
        outpars=False,
        vlsrcorr=0,
        extinction=False,
        parinfo=modelpars(),
        quiet=1,
        **kwargs):
    """ fit a model spectrum 
    The model is defined internal to fitspec so that parameters can
    be fixed based on input parameters """

    specsegments = copy(specsegments) # copy.deepcopy(specsegments)
    parinfo = copy(parinfo) # copy.deepcopy(parinfo)

    kwargs = {'extinction':extinction}
    if extinction:
        parinfo=modelpars(extinction=extinction)

    def fitfun(x,y,err):
        return lambda(p): (y-modelspec(x,*p,**kwargs))/err

    def mpfitfun(x,y,err):
        def f(p,fjac=None): return [0,(y-modelspec(x,*p,**kwargs))/err]
        return f

    if aperture is not None:
        miny,maxy = aperture

        fitdata = asarray([ss['data'][miny:maxy,:].sum(axis=0) for ss in specsegments]).ravel()
        fiterr = asarray([ss['err'][:]*sqrt(maxy-miny) for ss in specsegments]).ravel()   # since it is a summed aperture, add in quadrature
    else:
        fitdata = asarray([ss['data'] for ss in specsegments]).ravel()
        fiterr = asarray([ss['err'] for ss in specsegments]).ravel() 

    fitwl = asarray([ss['wavelength'] for ss in specsegments]).ravel()

    try:
        parinfo = parinfo.tolist()
        print "Parinfo was an array.  No idea why, it was never set to one.  Ever."
    except:
        pass

    mp = mpfit(mpfitfun(fitwl,fitdata,fiterr),parinfo=parinfo,quiet=quiet)
    mpp = mp.params
    mpperr = mp.perror

    for i,p in enumerate(mpp):
        parinfo[i]['value'] = p
        if parinfo[i]['parname'] == 'SHIFT':
            print parinfo[i]['parname'],p+vlsrcorr," +/- ",mpperr[i]
        else:
            print parinfo[i]['parname'],p," +/- ",mpperr[i]
#    print "Temperature: ",mpp[0]," Shift: ",mpp[3],mpp[3]*mean(fitwl)/3e5," Width: ",mpp[2],mpp[2]*mean(fitwl)/3e5,"   Ortho/Para: ",mpp[4]
#    print "ERRORS: Temperature: ",mpperr[0]," Shift: ",mpperr[3],mpperr[3]*mean(fitwl)/3e5," Width: ",mpperr[2],mpperr[2]*mean(fitwl)/3e5,"   Ortho/Para: ",mpperr[4]
    print "Chi2: ",mp.fnorm," Reduced Chi2: ",mp.fnorm/len(fitdata)," DOF:",len(fitdata)-len(mpp)

    apfitd = {
        'params':mpp,
        'parerr':mpperr,
        'parinfo':parinfo,
        'wl':fitwl,
        'data':fitdata,
        'model':modelspec(fitwl,*mpp),
        'err':fiterr
        }

    if aperture is not None:
        for ss in specsegments: ss['data']  = ss['data'][miny:maxy,:].sum(axis=0)
    for ss in specsegments: ss['model'] = modelspec(ss['wavelength'],*mpp)

    if outpars: return specsegments,apfitd,parinfo
    else: return specsegments,apfitd
Ejemplo n.º 15
0
def brokenpowerfit(xax,
                   data,
                   err=None,
                   alphaguess1=0.0,
                   alphaguess2=-2.0,
                   scaleguess=1.0,
                   breakpoint=None,
                   quiet=True):
    """
    Fit a broken power law (a line in log-space) to data as a function of x
    differs from 'plfit' because plfit fits a power law distribution, 
    this code simply fits a power law

    This is a lot more intricate than the simple power law fit, since it involves
    fitting two power laws with different slopes

    Parameters:
    p[0] - scale
    p[1] - breakpoint
    p[2] - power 1 (xax < breakpoint)
    p[3] - power 2 (xax >= breakpoint)

    There are 5 parameters (NOT 4) returned because there are two scales that are *NOT*
    independent

    returns: scale1,scale2,breakpoint,alpha1,alpha2

    """

    logdata = np.log10(data)
    if err is None: err = np.ones(data.shape, dtype='float')

    def brokenpowerlaw(p):
        lowerhalf = (np.log10(p[0]) + np.log10(xax) * p[2]) * (xax < p[1])
        # find the location at which both functions must be equal
        scale2loc = np.argmin(np.abs(xax - p[1]))
        scale2 = np.log10(xax[scale2loc]) * (p[2] - p[3]) + np.log10(p[0])
        upperhalf = (scale2 + np.log10(xax) * p[3]) * (xax >= p[1])
        # DEBUG print "scale1: %15g   scale2: %15g  xaxind: %5i  xaxval: %15g  lower: %15g upper: %15g" % (p[0],scale2,scale2loc,np.log10(xax[scale2loc]),lowerhalf[scale2loc-1],upperhalf[scale2loc])
        return lowerhalf + upperhalf

    def mpfitfun(data, err):
        def f(p, fjac=None):
            return [0, np.ravel((brokenpowerlaw(p) - data) / err)]

        return f

    if breakpoint is None: breakpoint = np.median(xax)

    parinfo = [{}, {
        'mpminstep': xax.min(),
        'mpmaxstep': xax.max(),
        'step': xax.min()
    }, {}, {}]

    mp = mpfit.mpfit(mpfitfun(logdata, err),
                     xall=[scaleguess, breakpoint, alphaguess1, alphaguess2],
                     quiet=quiet,
                     parinfo=parinfo)
    fitp = mp.params

    scale2loc = np.argmin(np.abs(xax - fitp[1]))
    scale2 = 10**(np.log10(xax[scale2loc]) * (fitp[2] - fitp[3]) +
                  np.log10(fitp[0]))
    fitp = np.array([fitp[0], scale2] + fitp[1:].tolist())

    return fitp, mp
Ejemplo n.º 16
0
def fit_sed_mpfit_hz(xdata,
                     flux,
                     guesses=(0, 0),
                     err=None,
                     blackbody_function='blackbody',
                     quiet=True,
                     sc=1e20,
                     **kwargs):
    """
    Parameters
    ----------
    xdata : array
        Array of the frequencies of the data
    flux : array
        The fluxes corresponding to the xdata values.  Should be in
        erg/s/cm^2/Hz
    guesses : (Temperature,Column) or (Temperature,Beta,Column)
        The input guesses.  3 parameters are used for modified blackbody
        fitting, two for temperature fitting.
    blackbody_function: str
        The blackbody function to fit, either 'blackbody', 'modified', or
        'modified_blackbody'
    quiet : bool
        quiet flag passed to mpfit
    sc : float
        A numerical parameter to enable the fitter to function properly.
        It is unclear what values this needs to take, 1e20 seems to work
        by bringing the units from erg/s/cm^2/Hz to Jy, i.e. bringing them
        into the "of order 1" regime.  This does NOT affect the output *units*,
        though it may affect the quality of the fit.

    Returns
    -------
    mp : mpfit structure
        An mpfit structure.  Access parameters and errors via
        `mp.params` and `mp.perror`.  The covariance matrix
        is in mp.covar.

    Examples
    --------
    >>> from astropy import units as u
    >>> import numpy as np
    >>> wavelengths = np.array([20,70,160,250,350,500,850,1100]) * u.um
    >>> frequencies = wavelengths.to(u.Hz, u.spectral())
    >>> temperature = 15 * u.K
    >>> column = 1e22 * u.cm**-2
    >>> flux = modified_blackbody(frequencies, temperature, beta=1.75,
    ...                           column=column)
    >>> err = 0.1 * flux
    >>> np.random.seed(0)
    >>> noise = np.random.randn(frequencies.size) * err
    >>> tguess, bguess, nguess = 20.,2.,21.5
    >>> bbunit = u.erg/u.s/u.cm**2/u.Hz
    >>> mp = fit_sed_mpfit_hz(frequencies.to(u.Hz).value,
    ...                       (flux+noise).to(bbunit).value, err=err.to(bbunit).value,
    ...                       blackbody_function='modified',
    ...                       guesses=(tguess, bguess, nguess))
    >>> print(mp.params)
    [ 14.99095224   1.78620237  22.05271119]
    >>> # T~14.9 K, beta ~1.79, column ~10^22
    """
    try:
        from agpy import mpfit
    except ImportError:
        print("Cannot import mpfit: cannot use mpfit-based fitter.")

    bbfd = {
        'blackbody': _blackbody_hz,
        'modified': _modified_blackbody_hz,
        'modified_blackbody': _modified_blackbody_hz
    }

    bbf = bbfd[blackbody_function]

    def mpfitfun(x, y, err):
        if err is None:

            def f(p, fjac=None):
                return [0, (y * sc - bbf(x, *p, **kwargs) * sc)]
        else:

            def f(p, fjac=None):
                return [0, (y * sc - bbf(x, *p, **kwargs) * sc) / (err * sc)]

        return f

    err = err if err is not None else flux * 0.0 + 1.0

    mp = mpfit.mpfit(mpfitfun(xdata, flux, err), guesses, quiet=quiet)

    return mp
Ejemplo n.º 17
0
def fit_sed_mpfit_hz(xdata, flux, guesses=(0,0), err=None,
                     blackbody_function='blackbody', quiet=True, sc=1e20,
                     **kwargs):
    """
    Parameters
    ----------
    xdata : array
        Array of the frequencies of the data
    flux : array
        The fluxes corresponding to the xdata values.  Should be in
        erg/s/cm^2/Hz
    guesses : (Temperature,Column) or (Temperature,Beta,Column)
        The input guesses.  3 parameters are used for modified blackbody
        fitting, two for temperature fitting.
    blackbody_function: str
        The blackbody function to fit, either 'blackbody', 'modified', or
        'modified_blackbody'
    quiet : bool
        quiet flag passed to mpfit
    sc : float
        A numerical parameter to enable the fitter to function properly.
        It is unclear what values this needs to take, 1e20 seems to work
        by bringing the units from erg/s/cm^2/Hz to Jy, i.e. bringing them
        into the "of order 1" regime.  This does NOT affect the output *units*,
        though it may affect the quality of the fit.

    Returns
    -------
    mp : mpfit structure
        An mpfit structure.  Access parameters and errors via
        `mp.params` and `mp.perror`.  The covariance matrix
        is in mp.covar.

    Examples
    --------
    >>> from astropy import units as u
    >>> import numpy as np
    >>> wavelengths = np.array([20,70,160,250,350,500,850,1100]) * u.um
    >>> frequencies = wavelengths.to(u.Hz, u.spectral())
    >>> temperature = 15 * u.K
    >>> column = 1e22 * u.cm**-2
    >>> flux = modified_blackbody(frequencies, temperature, beta=1.75,
    ...                           column=column)
    >>> err = 0.1 * flux
    >>> np.random.seed(0)
    >>> noise = np.random.randn(frequencies.size) * err
    >>> tguess, bguess, nguess = 20.,2.,21.5
    >>> bbunit = u.erg/u.s/u.cm**2/u.Hz
    >>> mp = fit_sed_mpfit_hz(frequencies.to(u.Hz).value,
    ...                       (flux+noise).to(bbunit).value, err=err.to(bbunit).value,
    ...                       blackbody_function='modified',
    ...                       guesses=(tguess, bguess, nguess))
    >>> print(mp.params)
    [ 14.99095224   1.78620237  22.05271119]
    >>> # T~14.9 K, beta ~1.79, column ~10^22
    """
    try:
        from agpy import mpfit
    except ImportError:
        print("Cannot import mpfit: cannot use mpfit-based fitter.")


    bbfd = {'blackbody': _blackbody_hz,
            'modified': _modified_blackbody_hz,
            'modified_blackbody': _modified_blackbody_hz}

    bbf = bbfd[blackbody_function]

    def mpfitfun(x,y,err):
        if err is None:
            def f(p,fjac=None):
                return [0,(y*sc-bbf(x, *p, **kwargs)*sc)]
        else:
            def f(p,fjac=None):
                return [0,(y*sc-bbf(x, *p, **kwargs)*sc)/(err*sc)]
        return f

    err = err if err is not None else flux*0.0 + 1.0

    mp = mpfit.mpfit(mpfitfun(xdata,flux,err), guesses, quiet=quiet)

    return mp
def multigaussfit(xax,
                  data,
                  ngauss=1,
                  err=None,
                  params=[1, 0, 1],
                  fixed=[False, False, False],
                  limitedmin=[False, False, True],
                  limitedmax=[False, False, False],
                  minpars=[0, 0, 0],
                  maxpars=[0, 0, 0],
                  quiet=True,
                  shh=True,
                  veryverbose=False):
    """
    An improvement on onedgaussfit.  Lets you fit multiple gaussians.

    Inputs:
       xax - x axis
       data - y axis
       ngauss - How many gaussians to fit?  Default 1 (this could supersede onedgaussfit)
       err - error corresponding to data

     These parameters need to have length = 3*ngauss.  If ngauss > 1 and length = 3, they will
     be replicated ngauss times, otherwise they will be reset to defaults:
       params - Fit parameters: [amplitude, offset, width] * ngauss
              If len(params) % 3 == 0, ngauss will be set to len(params) / 3
       fixed - Is parameter fixed?
       limitedmin/minpars - set lower limits on each parameter (default: width>0)
       limitedmax/maxpars - set upper limits on each parameter

       quiet - should MPFIT output each iteration?
       shh - output final parameters?

    Returns:
       Fit parameters
       Model
       Fit errors
       chi2
    """

    if len(params) != ngauss and (len(params) / 3) > ngauss:
        ngauss = len(params) / 3

    if isinstance(params, numpy.ndarray): params = params.tolist()

    # make sure all various things are the right length; if they're not, fix them using the defaults
    for parlist in (params, fixed, limitedmin, limitedmax, minpars, maxpars):
        if len(parlist) != 3 * ngauss:
            # if you leave the defaults, or enter something that can be multiplied by 3 to get to the
            # right number of gaussians, it will just replicate
            if len(parlist) == 3:
                parlist *= ngauss
            elif parlist == params:
                parlist[:] = [1, 0, 1] * ngauss
            elif parlist == fixed or parlist == limitedmax:
                parlist[:] = [False, False, False] * ngauss
            elif parlist == limitedmin:
                parlist[:] = [False, False, True] * ngauss
            elif parlist == minpars or parlist == maxpars:
                parlist[:] = [0, 0, 0] * ngauss

    def mpfitfun(x, y, err):
        if err is None:

            def f(p, fjac=None):
                return [0, (y - n_gaussian(pars=p)(x))]
        else:

            def f(p, fjac=None):
                return [0, (y - n_gaussian(pars=p)(x)) / err]

        return f

    if xax == None:
        xax = numpy.arange(len(data))

    parnames = {0: "AMPLITUDE", 1: "SHIFT", 2: "WIDTH"}

    parinfo = [{
        'n': ii,
        'value': params[ii],
        'limits': [minpars[ii], maxpars[ii]],
        'limited': [limitedmin[ii], limitedmax[ii]],
        'fixed': fixed[ii],
        'parname': parnames[ii % 3] + str(ii % 3),
        'error': ii
    } for ii in xrange(len(params))]

    if veryverbose:
        print "GUESSES: "
        print "\n".join(
            ["%s: %s" % (p['parname'], p['value']) for p in parinfo])

    mp = mpfit(mpfitfun(xax, data, err), parinfo=parinfo, quiet=quiet)
    mpp = mp.params
    mpperr = mp.perror
    chi2 = mp.fnorm

    if mp.status == 0:
        raise Exception(mp.errmsg)

    if not shh:
        print "Final fit values: "
        for i, p in enumerate(mpp):
            parinfo[i]['value'] = p
            print parinfo[i]['parname'], p, " +/- ", mpperr[i]
        print "Chi2: ", mp.fnorm, " Reduced Chi2: ", mp.fnorm / len(
            data), " DOF:", len(data) - len(mpp)

    return mpp, n_gaussian(pars=mpp)(xax), mpperr, chi2
Ejemplo n.º 19
0
    # p[9]... other Cosmological parameters


# initial conditions

print 'Prepare and reading data ...'
p0  = np.array([0.141 ,3.099 ,-19.10 ,-0.07 , 68.34 , 0.0221, 0.305,ogh2, 0],dtype='float64')
n   = len(p0)

parbase={'value':0., 'fixed':0, 'limited':[0,0], 'limits':[0.,0.]}
parinfo=[]


for i in range(n):
    parinfo.append(copy.deepcopy(parbase))
for i in range(n): 
    parinfo[i]['value']=p0[i]
    


m = mpfit(residual, p0, parinfo=parinfo)

if (m.status <= 0): 
    print 'error message = ', m.errmsg

chisq=(residual(m.params)[1]**2).sum()

print m.params
print m.perror

Ejemplo n.º 20
0
def psffit(data,err=None,params=[],autoderiv=True,return_all=False,circle=True,
        fixed=numpy.repeat(False,7),limitedmin=[False,False,False,False,True,True,True],
        limitedmax=[False,False,False,False,False,False,True],
        usemoment=numpy.array([],dtype='bool'),
        minpars=numpy.repeat(0,7),maxpars=[0,0,0,0,0,0,360],
        rotate=0,vheight=1,quiet=True,returnmp=False,
        returnfitimage=False,
        psffunction=airy, 
        extra_pars=None,
        return_parinfo=False,
        **kwargs):
    """
    PSF fitter with the ability to fit a variety of different forms of
    2-dimensional gaussian OR an Airy.
    This code is mostly directly copied from gaussfitter.py and presents 
    yet another argument for me turning this into a class...
    
    Input Parameters:
        data - 2-dimensional data array
        err=None - error array with same size as data array
        params=[] - initial input parameters for Gaussian function.
            (height, amplitude, x, y, width_x, width_y, rota)
            if not input, these will be determined from the moments of the system, 
            assuming no rotation
        autoderiv=1 - use the autoderiv provided in the lmder.f function (the
            alternative is to us an analytic derivative with lmdif.f: this method
            is less robust)
        return_all=0 - Default is to return only the Gaussian parameters.  
                   1 - fit params, fit error
        returnfitimage - returns (best fit params,best fit image)
        returnmp - returns the full mpfit struct
        circle=0 - default is an elliptical gaussian (different x, y widths),
            but can reduce the input by one parameter if it's a circular gaussian
        rotate=1 - default allows rotation of the gaussian ellipse.  Can remove
            last parameter by setting rotate=0.  numpy.expects angle in DEGREES
        vheight=1 - default allows a variable height-above-zero, i.e. an
            additive constant for the Gaussian function.  Can remove first
            parameter by setting this to 0
        usemoment - can choose which parameters to use a moment estimation for.
            Other parameters will be taken from params.  Needs to be a boolean
            array.
        extra_pars - If your psffunction requires extra parameters, pass their
            parinfo dictionaries through this variable

    Output:
        Default output is a set of Gaussian parameters with the same shape as
            the input parameters

        Can also output the covariance matrix, 'infodict' that contains a lot
            more detail about the fit (see scipy.optimize.leastsq), and a message
            from leastsq telling what the exit status of the fitting routine was

        Warning: Does NOT necessarily output a rotation angle between 0 and 360 degrees.
    """
    usemoment=numpy.array(usemoment,dtype='bool')
    params=numpy.array(params,dtype='float')
    if usemoment.any() and len(params)==len(usemoment):
        moment = numpy.array(moments(data,circle,rotate,vheight,**kwargs),dtype='float')
        params[usemoment] = moment[usemoment]
    elif params == [] or len(params)==0:
        params = (moments(data,circle,rotate,vheight,**kwargs))
    if vheight==0:
        vheight=1
        params = numpy.concatenate([[0],params])
        fixed[0] = 1


    # mpfit will fail if it is given a start parameter outside the allowed range:
    for i in xrange(len(params)): 
        if params[i] > maxpars[i] and limitedmax[i]: params[i] = maxpars[i]
        if params[i] < minpars[i] and limitedmin[i]: params[i] = minpars[i]

    if err is None:
        errorfunction = lambda p: numpy.ravel((psffunction(p,circle,rotate,vheight)\
                (*numpy.indices(data.shape)) - data))
    else:
        errorfunction = lambda p: numpy.ravel((psffunction(p,circle,rotate,vheight)\
                (*numpy.indices(data.shape)) - data)/err)
    def mpfitfun(data,err):
        if err is None:
            def f(p,fjac=None): return [0,numpy.ravel(data-psffunction(p,circle,rotate,vheight)\
                    (*numpy.indices(data.shape)))]
        else:
            def f(p,fjac=None): return [0,numpy.ravel((data-psffunction(p,circle,rotate,vheight)\
                    (*numpy.indices(data.shape)))/err)]
        return f

                    
    parinfo = [ ]
    if vheight:
        parinfo.insert(0,{'n':0,'value':params[0],'limits':[minpars[0],maxpars[0]],'limited':[limitedmin[0],limitedmax[0]],'fixed':fixed[0],'parname':"HEIGHT",'error':0})
        ind = 1
    else:
        ind = 0

    parinfo.append({'n':0+ind,'value':params[0+ind],'limits':[minpars[0+ind],maxpars[0+ind]],'limited':[limitedmin[0+ind],limitedmax[0+ind]],'fixed':fixed[0+ind],'parname':"AMPLITUDE",'error':0})
    parinfo.append({'n':1+ind,'value':params[1+ind],'limits':[minpars[1+ind],maxpars[1+ind]],'limited':[limitedmin[1+ind],limitedmax[1+ind]],'fixed':fixed[1+ind],'parname':"XSHIFT",'error':0})
    parinfo.append({'n':2+ind,'value':params[2+ind],'limits':[minpars[2+ind],maxpars[2+ind]],'limited':[limitedmin[2+ind],limitedmax[2+ind]],'fixed':fixed[2+ind],'parname':"YSHIFT",'error':0})
    parinfo.append({'n':3+ind,'value':params[3+ind],'limits':[minpars[3+ind],maxpars[3+ind]],'limited':[limitedmin[3+ind],limitedmax[3+ind]],'fixed':fixed[3+ind],'parname':"XWIDTH",'error':0})

    if not(circle):
        parinfo.append({'n':4+ind,'value':params[4+ind],'limits':[minpars[4+ind],maxpars[4+ind]],'limited':[limitedmin[4+ind],limitedmax[4+ind]],'fixed':fixed[4+ind],'parname':"YWIDTH",'error':0})
        if rotate:
            parinfo.append({'n':5+ind,'value':params[5+ind],'limits':[minpars[5+ind],maxpars[5+ind]],'limited':[limitedmin[5+ind],limitedmax[5+ind]],'fixed':fixed[5+ind],'parname':"ROTATION",'error':0})

    if extra_pars:
        for P in extra_pars:
            parinfo.append(P)

    if autoderiv is False:
        # the analytic derivative, while not terribly difficult, is less
        # efficient and useful.  I only bothered putting it here because I was
        # instructed to do so for a class project - please ask if you would
        # like this feature implemented
        raise NotImplementedError("I'm sorry, I haven't implemented this feature yet.")
    else:
#        p, cov, infodict, errmsg, success = optimize.leastsq(errorfunction,\
#                params, full_output=1)
        mp = mpfit(mpfitfun(data,err),parinfo=parinfo,quiet=quiet)


    if returnmp:
        returns = (mp)
    elif return_parinfo:
        returns = (parinfo)
    elif return_all is False:
        returns = mp.params
    elif return_all:
        returns = mp.params,mp.perror
    if returnfitimage:
        fitimage = psffunction(mp.params,circle,rotate,vheight)(*numpy.indices(data.shape))
        returns = (returns,fitimage)
    return returns
    def fit_blackbody(xdata,
                      flux,
                      guesses=(0, 0),
                      err=None,
                      blackbody_function=blackbody,
                      quiet=True,
                      **kwargs):
        """
        Parameters
        ----------
        xdata : array
            Array of the X-values (frequency, wavelength) of the data
        flux : array
            The fluxes corresponding to the xdata values
        guesses : (Temperature,Scale) or (Temperature,Beta,Scale)
            The input guesses.  3 parameters are used for greybody 
            fitting, two for temperature fitting.
        blackbody_function: function
            Must take x-axis (e.g. frequency), temperature, scale, and then
            optionally beta args 
        quiet : bool
            quiet flag passed to mpfit

        Returns
        -------
        mp : mpfit structure
            An mpfit structure.  Access parameters and errors via
            `mp.params` and `mp.perror`.  The covariance matrix
            is in mp.covar.

        Examples
        --------
        >>> wavelength = array([20,70,160,250,350,500,850,1100])
        >>> flux = modified_blackbody_wavelength(wavelength, 15, beta=1.75,
                logN=22, wavelength_units='microns', normalize=False,
                logscale=16)
        >>> err = 0.1 * flux
        >>> np.random.seed(0)
        >>> flux += np.random.randn(len(wavelength)) * err
        >>> tguess, bguess, nguess = 20.,2.,21.5
        >>> mp = fit_blackbody(wavelength, flux, err=err,
                 blackbody_function=modified_blackbody_wavelength,
                 logscale=16, guesses=(tguess, bguess, nguess),
                 wavelength_units='microns')
        >>> print mp.params 
        [ 14.99095224   1.78620237  22.05271119]
        >>> # T~14.9 K, beta ~1.79, column ~10^22
        """
        def mpfitfun(x, y, err):
            if err is None:

                def f(p, fjac=None):
                    return [
                        0,
                        (y -
                         blackbody_function(x, *p, normalize=False, **kwargs))
                    ]
            else:

                def f(p, fjac=None):
                    return [
                        0,
                        (y - blackbody_function(
                            x, *p, normalize=False, **kwargs)) / err
                    ]

            return f

        err = err if err is not None else flux * 0.0 + 1.0

        mp = mpfit.mpfit(mpfitfun(xdata, flux, err), guesses, quiet=quiet)

        return mp
def onedgaussfit(xax,
                 data,
                 err=None,
                 params=[0, 1, 0, 1],
                 fixed=[False, False, False, False],
                 limitedmin=[False, False, False, True],
                 limitedmax=[False, False, False, False],
                 minpars=[0, 0, 0, 0],
                 maxpars=[0, 0, 0, 0],
                 quiet=True,
                 shh=True,
                 veryverbose=False,
                 vheight=True,
                 negamp=False,
                 usemoments=False):
    """
    Inputs:
       xax - x axis
       data - y axis
       err - error corresponding to data

       params - Fit parameters: Height of background, Amplitude, Shift, Width
       fixed - Is parameter fixed?
       limitedmin/minpars - set lower limits on each parameter (default: width>0)
       limitedmax/maxpars - set upper limits on each parameter
       quiet - should MPFIT output each iteration?
       shh - output final parameters?
       usemoments - replace default parameters with moments

    Returns:
       Fit parameters
       Model
       Fit errors
       chi2
    """
    def mpfitfun(x, y, err):
        if err is None:

            def f(p, fjac=None):
                return [0, (y - onedgaussian(x, *p))]
        else:

            def f(p, fjac=None):
                return [0, (y - onedgaussian(x, *p)) / err]

        return f

    if xax == None:
        xax = numpy.arange(len(data))

    if vheight is False:
        height = params[0]
        fixed[0] = True
    if usemoments:
        params = onedmoments(xax,
                             data,
                             vheight=vheight,
                             negamp=negamp,
                             veryverbose=veryverbose)
        if vheight is False: params = [height] + params
        if veryverbose:
            print "OneD moments: h: %g  a: %g  c: %g  w: %g" % tuple(params)

    parinfo = [{
        'n': 0,
        'value': params[0],
        'limits': [minpars[0], maxpars[0]],
        'limited': [limitedmin[0], limitedmax[0]],
        'fixed': fixed[0],
        'parname': "HEIGHT",
        'error': 0
    }, {
        'n': 1,
        'value': params[1],
        'limits': [minpars[1], maxpars[1]],
        'limited': [limitedmin[1], limitedmax[1]],
        'fixed': fixed[1],
        'parname': "AMPLITUDE",
        'error': 0
    }, {
        'n': 2,
        'value': params[2],
        'limits': [minpars[2], maxpars[2]],
        'limited': [limitedmin[2], limitedmax[2]],
        'fixed': fixed[2],
        'parname': "SHIFT",
        'error': 0
    }, {
        'n': 3,
        'value': params[3],
        'limits': [minpars[3], maxpars[3]],
        'limited': [limitedmin[3], limitedmax[3]],
        'fixed': fixed[3],
        'parname': "WIDTH",
        'error': 0
    }]

    mp = mpfit(mpfitfun(xax, data, err), parinfo=parinfo, quiet=quiet)
    mpp = mp.params
    mpperr = mp.perror
    chi2 = mp.fnorm

    if mp.status == 0:
        raise Exception(mp.errmsg)

    if (not shh) or veryverbose:
        print "Fit status: ", mp.status
        for i, p in enumerate(mpp):
            parinfo[i]['value'] = p
            print parinfo[i]['parname'], p, " +/- ", mpperr[i]
        print "Chi2: ", mp.fnorm, " Reduced Chi2: ", mp.fnorm / len(
            data), " DOF:", len(data) - len(mpp)

    return mpp, onedgaussian(xax, *mpp), mpperr, chi2