Exemplo n.º 1
0
    def fit_blackbody(xdata,
                      flux,
                      guesses=(0, 0),
                      err=None,
                      blackbody_function=blackbody,
                      quiet=True,
                      **kwargs):
        """
        guesses = Temperature, Arbitrary Scale
        OR Temperature, Beta, Arbitrary Scale
        """
        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
Exemplo n.º 2
0
    def multinh3fit(self, xax, data, err=None,
                    parinfo=None,
                    quiet=True, shh=True,
                    debug=False,
                    maxiter=200,
                    use_lmfit=False,
                    veryverbose=False, **kwargs):
        """
        Fit multiple nh3 profiles (multiple can be 1)

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

         These parameters need to have length = 6*npeaks.  If npeaks > 1 and length = 6, they will
         be replicated npeaks times, otherwise they will be reset to defaults:
           params - Fit parameters: [tkin, tex, ntot (or tau), width, offset, ortho fraction] * npeaks
                  If len(params) % 6 == 0, npeaks will be set to len(params) / 6
           fixed - Is parameter fixed?
           limitedmin/minpars - set lower limits on each parameter (default: width>0, Tex and Tkin > Tcmb)
           limitedmax/maxpars - set upper limits on each parameter
           parnames - default parameter names, important for setting kwargs in model ['tkin','tex','ntot','width','xoff_v','fortho']

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

        Returns:
           Fit parameters
           Model
           Fit errors
           chi2
        """

        if parinfo is None:
            parinfo = self.parinfo = self.make_parinfo(**kwargs)
        else: 
            if isinstance(parinfo, ParinfoList):
                if not quiet:
                    log.info("Using user-specified parinfo.")
                self.parinfo = parinfo
            else:
                if not quiet:
                    log.info("Using something like a user-specified parinfo, but not.")
                self.parinfo = ParinfoList([p if isinstance(p,Parinfo) else Parinfo(p)
                                            for p in parinfo],
                                           preserve_order=True) 

        fitfun_kwargs = dict((x,y) for (x,y) in kwargs.items()
                             if x not in ('npeaks', 'params', 'parnames',
                                          'fixed', 'limitedmin', 'limitedmax',
                                          'minpars', 'maxpars', 'tied',
                                          'max_tem_step'))
        if 'use_lmfit' in fitfun_kwargs:
            raise KeyError("use_lmfit was specified in a location where it "
                           "is unacceptable")

        npars = len(parinfo)/self.npeaks

        self._validate_parinfo()

        def mpfitfun(x,y,err):
            if err is None:
                def f(p,fjac=None): return [0,(y-self.n_ammonia(pars=p,
                                                                parnames=parinfo.parnames,
                                                                **fitfun_kwargs)(x))]
            else:
                def f(p,fjac=None): return [0,(y-self.n_ammonia(pars=p,
                                                                parnames=parinfo.parnames,
                                                                **fitfun_kwargs)(x))/err]
            return f

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

        if use_lmfit:
            return self.lmfitter(xax, data, err=err,
                                 parinfo=parinfo,
                                 quiet=quiet,
                                 debug=debug)
        else:
            mp = mpfit(mpfitfun(xax,data,err),
                       parinfo=parinfo,
                       maxiter=maxiter,
                       quiet=quiet,
                       debug=debug)
            mpp = mp.params
            if mp.perror is not None: mpperr = mp.perror
            else: mpperr = mpp*0
            chi2 = mp.fnorm

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

        for i,p in enumerate(mpp):
            parinfo[i]['value'] = p
            parinfo[i]['error'] = mpperr[i]

        if not shh:
            log.info("Fit status: {0}".format(mp.status))
            log.info("Fit message: {0}".format(mpfit_messages[mp.status]))
            log.info("Fit error message: {0}".format(mp.errmsg))
            log.info("Final fit values: ")
            for i,p in enumerate(mpp):
                log.info(" ".join((parinfo[i]['parname'], str(p), " +/- ",
                                   str(mpperr[i]))))
            log.info(" ".join(("Chi2: ", str(mp.fnorm)," Reduced Chi2: ",
                               str(mp.fnorm/len(data)), " DOF:",
                               str(len(data)-len(mpp)))))

        self.mp = mp

        self.parinfo = parinfo

        self.mpp = self.parinfo.values
        self.mpperr = self.parinfo.errors
        self.mppnames = self.parinfo.names
        self.model = self.n_ammonia(pars=self.mpp, parnames=self.mppnames,
                                    **fitfun_kwargs)(xax)

        indiv_parinfo = [self.parinfo[jj*self.npars:(jj+1)*self.npars]
                         for jj in xrange(len(self.parinfo)/self.npars)]
        modelkwargs = [
                dict([(p['parname'].strip("0123456789").lower(),p['value']) for p in pi])
                for pi in indiv_parinfo]
        self.tau_list = [ammonia(xax,return_tau=True,**mk) for mk in modelkwargs]

        return self.mpp,self.model,self.mpperr,chi2
Exemplo n.º 3
0
    def _baseline(self, spectrum, xarr=None, err=None,
                  order=1, quiet=True, masktoexclude=None, powerlaw=False,
                  xarr_fit_units='pixels', LoudDebug=False, renormalize='auto',
                  zeroerr_is_OK=True, spline=False, **kwargs):
        """
        Fit a baseline/continuum to a spectrum

        Parameters
        ----------
        masktoexclude : boolean array
            True: will not be fit
            False: will be fit
            *if ALL are True, ALL pixels will be fit - just with silly weights*
        """

        #if xmin == 'default':
        #    if order <= 1 and mask is None: xmin = np.floor( spectrum.shape[-1]*0.1 )
        #    else: xmin = 0
        #elif xmin is None:
        #    xmin = 0
        #if xmax == 'default':
        #    if order <= 1 and mask is None: xmax = np.ceil( spectrum.shape[-1]*0.9 )
        #    else: xmax = spectrum.shape[-1]
        #elif xmax is None:
        #    xmax = spectrum.shape[-1]

        if xarr is None:
            xarr = np.indices(spectrum.shape).squeeze()


        # A good alternate implementation of masking is to only pass mpfit the data
        # that is unmasked.  That would require some manipulation above...
        if err is None:
            err = np.ones(spectrum.shape)
        else:
            # don't overwrite error
            err = err.copy()
            # assume anything with 0 error is GOOD
            if zeroerr_is_OK:
                err[err == 0] = 1.
            else: # flag it out!
                err[err == 0] = 1e10


        #err[:xmin] = 1e10
        #err[xmax:] = 1e10
        if masktoexclude is not None:
            if masktoexclude.dtype.name != 'bool':
                masktoexclude = masktoexclude.astype('bool')
            err[masktoexclude] = 1e10
            if LoudDebug: print "In _baseline: %i points masked out" % masktoexclude.sum()
        if (spectrum!=spectrum).sum() > 0:
            print "There is an error in baseline: some values are NaN"
            import pdb; pdb.set_trace()

        #xarrconv = xarr[xmin:xmax].as_unit(xarr_fit_units)
        OK = True-masktoexclude
        xarrconv = xarr.as_unit(xarr_fit_units)
        if powerlaw:
            # for powerlaw fitting, only consider positive data
            OK *= spectrum > 0
            pguess = [np.median(spectrum[OK]),2.0]
            if LoudDebug: print "_baseline powerlaw Guesses: ",pguess

            def mpfitfun(data,err):
                #def f(p,fjac=None): return [0,np.ravel(((p[0] * (xarrconv[OK]-p[2])**(-p[1]))-data)/err)]
                # Logarithmic fitting:
                def f(p,fjac=None):
                    #return [0,
                    #        np.ravel( (np.log10(data) - np.log10(p[0]) + p[1]*np.log10(xarrconv[OK]/p[2])) / (err/data) )
                    #        ]
                    return [0, np.ravel( (data - self.get_model(xarr=xarrconv[OK],baselinepars=p)) / (err/data) )]
                return f
        else:
            pguess = [0]*(order+1)
            if LoudDebug: print "_baseline Guesses: ",pguess

            def mpfitfun(data,err):
                def f(p,fjac=None): return [0,np.ravel((np.poly1d(p)(xarrconv[OK])-data)/err)]
                return f
        #scalefactor = 1.0
        #if renormalize in ('auto',True):
        #    datarange = spectrum.max() - spectrum.min()
        #    if abs(datarange) < 1e-9 or abs(datarange) > 1e9:
        #        scalefactor = np.median(np.abs(self.spectrum))
        #        print "BASELINE: Renormalizing data by factor %e to improve fitting procedure" % scalefactor
        #        spectrum /= scalefactor
        #        err /= scalefactor

        import pyspeckit.mpfit as mpfit
        mp = mpfit.mpfit(mpfitfun(spectrum[OK],err[OK]),xall=pguess,quiet=quiet) # mpfit doesn't need to take kwargs, I think ,**kwargs)
        if np.isnan(mp.fnorm):
            raise ValueError("chi^2 is NAN in baseline fitting")
        fitp = mp.params
        if powerlaw:
            #bestfit = (fitp[0]*(xarrconv)**(-fitp[1])).squeeze()
            bestfit = (fitp[0]*(xarrconv)**(-fitp[1])).squeeze()
        else:
            bestfit = np.poly1d(fitp)(xarrconv).squeeze()

        return bestfit,fitp
Exemplo n.º 4
0
    def fitter(self,
               xax,
               data,
               err=None,
               quiet=True,
               veryverbose=False,
               debug=False,
               parinfo=None,
               **kwargs):
        """
        Run the fitter using mpfit.
        
        kwargs will be passed to _make_parinfo and mpfit.

        Parameters
        ----------
        xax : SpectroscopicAxis 
            The X-axis of the spectrum
        data : ndarray
            The data to fit
        err : ndarray (optional)
            The error on the data.  If unspecified, will be uniform unity
        parinfo : ParinfoList
            The guesses, parameter limits, etc.  See
            `pyspeckit.spectrum.parinfo` for details
        quiet : bool
            pass to mpfit.  If False, will print out the parameter values for
            each iteration of the fitter
        veryverbose : bool
            print out a variety of mpfit output parameters
        debug : bool
            raise an exception (rather than a warning) if chi^2 is nan
        """

        if parinfo is None:
            parinfo, kwargs = self._make_parinfo(debug=debug, **kwargs)
        else:
            log.debug("Using user-specified parinfo dict")
            # clean out disallowed kwargs (don't want to pass them to mpfit)
            #throwaway, kwargs = self._make_parinfo(debug=debug, **kwargs)

        self.xax = xax  # the 'stored' xax is just a link to the original
        if hasattr(xax, 'as_unit') and self.fitunits is not None:
            # some models will depend on the input units.  For these, pass in an X-axis in those units
            # (gaussian, voigt, lorentz profiles should not depend on units.  Ammonia, formaldehyde,
            # H-alpha, etc. should)
            xax = copy.copy(xax)
            # xax.convert_to_unit(self.fitunits, quiet=quiet)
            xax = xax.as_unit(self.fitunits, quiet=quiet, **kwargs)
        elif self.fitunits is not None:
            raise TypeError("X axis does not have a convert method")

        if np.any(np.isnan(data)) or np.any(np.isinf(data)):
            err[np.isnan(data) + np.isinf(data)] = np.inf
            data[np.isnan(data) + np.isinf(data)] = 0

        if np.any(np.isnan(err)):
            raise ValueError(
                "One or more of the error values is NaN."
                "  This is not allowed.  Errors can be infinite "
                "(which is equivalent to giving zero weight to "
                "a data point), but otherwise they must be positive "
                "floats.")
        elif np.any(err < 0):
            raise ValueError("At least one error value is negative, which is "
                             "not allowed as negative errors are not "
                             "meaningful in the optimization process.")

        for p in parinfo:
            log.debug(p)
        log.debug("\n".join([
            "%s %i: tied: %s value: %s" %
            (p['parname'], p['n'], p['tied'], p['value']) for p in parinfo
        ]))

        mp = mpfit(self.mpfitfun(xax, data, err),
                   parinfo=parinfo,
                   quiet=quiet,
                   debug=debug,
                   **kwargs)
        mpp = mp.params
        if mp.perror is not None: mpperr = mp.perror
        else: mpperr = mpp * 0
        chi2 = mp.fnorm

        if mp.status == 0:
            if "parameters are not within PARINFO limits" in mp.errmsg:
                log.warn(parinfo)
            raise mpfitException(mp.errmsg)

        for i, (p, e) in enumerate(zip(mpp, mpperr)):
            self.parinfo[i]['value'] = p
            self.parinfo[i]['error'] = e

        if veryverbose:
            log.info("Fit status: {0}".format(mp.status))
            log.info("Fit error message: {0}".format(mp.errmsg))
            log.info("Fit message: {0}".format(mpfit_messages[mp.status]))
            for i, p in enumerate(mpp):
                log.info("{0}: {1} +/- {2}".format(self.parinfo[i]['parname'],
                                                   p, mpperr[i]))
            log.info("Chi2: {0} Reduced Chi2: {1}  DOF:{2}".format(
                mp.fnorm, mp.fnorm / (len(data) - len(mpp)),
                len(data) - len(mpp)))

        self.mp = mp
        self.mpp = self.parinfo.values
        self.mpperr = self.parinfo.errors
        self.mppnames = self.parinfo.names
        self.model = self.n_modelfunc(self.parinfo,
                                      **self.modelfunc_kwargs)(xax)
        log.debug("Modelpars: {0}".format(self.mpp))
        if np.isnan(chi2):
            if debug:
                raise ValueError("Error: chi^2 is nan")
            else:
                log.warn("Warning: chi^2 is nan")
        return mpp, self.model, mpperr, chi2
Exemplo n.º 5
0
    def multinh3fit(self, xax, data, npeaks=1, err=None, params=[20,20,1e10,1.0,0.0,0.5],
            fixed=[False,False,False,False,False,False],
            limitedmin=[True,True,True,True,False,True],
            limitedmax=[False,False,False,False,False,True], minpars=[2.73,2.73,0,0,0,0],
            maxpars=[0,0,0,0,0,1], quiet=True, shh=True, veryverbose=False, **kwargs):
        """
        Fit multiple nh3 profiles

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

         These parameters need to have length = 6*npeaks.  If npeaks > 1 and length = 6, they will
         be replicated npeaks times, otherwise they will be reset to defaults:
           params - Fit parameters: [amplitude, offset, Gfwhm, Lfwhm] * npeaks
                  If len(params) % 6 == 0, npeaks will be set to len(params) / 6
           fixed - Is parameter fixed?
           limitedmin/minpars - set lower limits on each parameter (default: width>0, Tex and Tkin > Tcmb)
           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
        """

        self.npars = 6

        if len(params) != npeaks and (len(params) / self.npars) > npeaks:
            npeaks = len(params) / self.npars 
        self.npeaks = npeaks

        if isinstance(params,np.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) != self.npars*self.npeaks:
                # 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) == self.npars: 
                    parlist *= npeaks 
                elif parlist==params:
                    parlist[:] = [20,20,1e10,1.0,0.0,0.5] * npeaks
                elif parlist==fixed:
                    parlist[:] = [False,False,False,False,False,False] * npeaks
                elif parlist==limitedmax:
                    parlist[:] = [False,False,False,False,False,True] * npeaks
                elif parlist==limitedmin:
                    parlist[:] = [True,True,True,True,False,True] * npeaks
                elif parlist==minpars:
                    parlist[:] = [2.73,0,0,0,0,0] * npeaks
                elif parlist==maxpars:
                    parlist[:] = [0,0,0,0,0,1] * npeaks

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

        parnames = {0:"TKIN",1:"TEX",2:"NTOT",3:"WIDTH",4:"XOFF_V",5:"FORTHO"}

        parinfo = [ {'n':ii, 'value':params[ii],
            'limits':[minpars[ii],maxpars[ii]],
            'limited':[limitedmin[ii],limitedmax[ii]], 'fixed':fixed[ii],
            'parname':parnames[ii%self.npars]+str(ii/self.npars), 
            'mpmaxstep':0,'error':ii} 
            for ii in xrange(len(params)) ]
        parinfo[0]['mpmaxstep'] = 1.0
        parinfo[1]['mpmaxstep'] = 1.0

        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
        if mp.perror is not None: mpperr = mp.perror
        else: mpperr = mpp*0
        chi2 = mp.fnorm

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

        if not shh:
            print "Fit message: ",mp.errmsg
            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)

        if mpp[1] > mpp[0]: mpp[1] = mpp[0]  # force Tex>Tkin to Tex=Tkin (already done in n_ammonia)
        self.mp = mp
        self.mpp = mpp
        self.mpperr = mpperr
        self.model = self.n_ammonia(pars=mpp,**kwargs)(xax)
        return mpp,self.n_ammonia(pars=mpp,**kwargs)(xax),mpperr,chi2
Exemplo n.º 6
0
    def multinh3fit(self, xax, data, npeaks=1, err=None, params=[20,20,1e10,1.0,0.0,0.5],
            fixed=[False,False,False,False,False,False],
            limitedmin=[True,True,True,True,False,True],
            limitedmax=[False,False,False,False,False,True], minpars=[2.73,2.73,0,0,0,0],
            maxpars=[0,0,0,0,0,1], quiet=True, shh=True, veryverbose=False, **kwargs):
        """
        Fit multiple nh3 profiles

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

         These parameters need to have length = 6*npeaks.  If npeaks > 1 and length = 6, they will
         be replicated npeaks times, otherwise they will be reset to defaults:
           params - Fit parameters: [amplitude, offset, Gfwhm, Lfwhm] * npeaks
                  If len(params) % 6 == 0, npeaks will be set to len(params) / 6
           fixed - Is parameter fixed?
           limitedmin/minpars - set lower limits on each parameter (default: width>0, Tex and Tkin > Tcmb)
           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
        """

        self.npars = 6

        if len(params) != npeaks and (len(params) / self.npars) > npeaks:
            npeaks = len(params) / self.npars 
        self.npeaks = npeaks

        if isinstance(params,np.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) != self.npars*self.npeaks:
                # 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) == self.npars: 
                    parlist *= npeaks 
                elif parlist==params:
                    parlist[:] = [20,20,1e10,1.0,0.0,0.5] * npeaks
                elif parlist==fixed:
                    parlist[:] = [False,False,False,False,False,False] * npeaks
                elif parlist==limitedmax:
                    parlist[:] = [False,False,False,False,False,True] * npeaks
                elif parlist==limitedmin:
                    parlist[:] = [True,True,True,True,False,True] * npeaks
                elif parlist==minpars:
                    parlist[:] = [2.73,0,0,0,0,0] * npeaks
                elif parlist==maxpars:
                    parlist[:] = [0,0,0,0,0,1] * npeaks

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

        parnames = {0:"TKIN",1:"TEX",2:"NTOT",3:"WIDTH",4:"XOFF_V",5:"FORTHO"}

        parinfo = [ {'n':ii, 'value':params[ii],
            'limits':[minpars[ii],maxpars[ii]],
            'limited':[limitedmin[ii],limitedmax[ii]], 'fixed':fixed[ii],
            'parname':parnames[ii%self.npars]+str(ii/self.npars), 
            'mpmaxstep':0,'error':ii} 
            for ii in xrange(len(params)) ]
        parinfo[0]['mpmaxstep'] = 1.0
        parinfo[1]['mpmaxstep'] = 1.0

        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
        if mp.perror is not None: mpperr = mp.perror
        else: mpperr = mpp*0
        chi2 = mp.fnorm

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

        if not shh:
            print "Fit message: ",mp.errmsg
            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)

        if mpp[1] > mpp[0]: mpp[1] = mpp[0]  # force Tex>Tkin to Tex=Tkin (already done in n_ammonia)
        self.mp = mp
        self.mpp = mpp
        self.mpperr = mpperr
        self.model = self.n_ammonia(pars=mpp,**kwargs)(xax)
        return mpp,self.n_ammonia(pars=mpp,**kwargs)(xax),mpperr,chi2
Exemplo n.º 7
0
    def multivoigtfit(self,xax, data, npeaks=1, err=None, params=[1,0,1,1],
            fixed=[False,False,False,False],
            limitedmin=[False,False,True,True],
            limitedmax=[False,False,False,False], minpars=[0,0,0,0],
            maxpars=[0,0,0,0], quiet=True, shh=True, veryverbose=False,
            **kwargs):
        """
        Fit multiple voigt profiles

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

         These parameters need to have length = 3*npeaks.  If npeaks > 1 and length = 3, they will
         be replicated npeaks times, otherwise they will be reset to defaults:
           params - Fit parameters: [amplitude, offset, Gfwhm, Lfwhm] * npeaks
                  If len(params) % 4 == 0, npeaks 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) != npeaks and (len(params) / 4) > npeaks:
            npeaks = len(params) / 4 
        self.npeaks = npeaks

        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) != 4*npeaks:
                # 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) == 4: 
                    parlist *= npeaks 
                elif parlist==params:
                    parlist[:] = [1,0,1,1] * npeaks
                elif parlist==fixed or parlist==limitedmax:
                    parlist[:] = [False,False,False,False] * npeaks
                elif parlist==limitedmin:
                    parlist[:] = [False,False,True,True] * npeaks
                elif parlist==minpars or parlist==maxpars:
                    parlist[:] = [0,0,0,0] * npeaks

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

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

        parnames = {0:"AMPLITUDE",1:"SHIFT",2:"GFWHM",3:"LFWHM"}

        parinfo = [ {'n':ii, 'value':params[ii],
            'limits':[minpars[ii],maxpars[ii]],
            'limited':[limitedmin[ii],limitedmax[ii]], 'fixed':fixed[ii],
            'parname':parnames[ii%4]+str(ii/4), '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
        if mp.perror is not None: mpperr = mp.perror
        else: mpperr = mpp*0
        chi2 = mp.fnorm

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

        if not shh:
            print "Fit message ",mp.errmsg
            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)

        self.mp = mp
        self.mpp = mpp
        self.mpperr = mpperr
        self.model = self.n_voigt(pars=mpp)(xax) 
        return mpp,self.n_voigt(pars=mpp)(xax),mpperr,chi2
Exemplo n.º 8
0
    def multivoigtfit(self,
                      xax,
                      data,
                      npeaks=1,
                      err=None,
                      params=[1, 0, 1, 1],
                      fixed=[False, False, False, False],
                      limitedmin=[False, False, True, True],
                      limitedmax=[False, False, False, False],
                      minpars=[0, 0, 0, 0],
                      maxpars=[0, 0, 0, 0],
                      quiet=True,
                      shh=True,
                      veryverbose=False,
                      **kwargs):
        """
        Fit multiple voigt profiles

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

         These parameters need to have length = 3*npeaks.  If npeaks > 1 and length = 3, they will
         be replicated npeaks times, otherwise they will be reset to defaults:
           params - Fit parameters: [amplitude, offset, Gfwhm, Lfwhm] * npeaks
                  If len(params) % 4 == 0, npeaks 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) != npeaks and (len(params) / 4) > npeaks:
            npeaks = len(params) / 4
        self.npeaks = npeaks

        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) != 4 * npeaks:
                # 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) == 4:
                    parlist *= npeaks
                elif parlist == params:
                    parlist[:] = [1, 0, 1, 1] * npeaks
                elif parlist == fixed or parlist == limitedmax:
                    parlist[:] = [False, False, False, False] * npeaks
                elif parlist == limitedmin:
                    parlist[:] = [False, False, True, True] * npeaks
                elif parlist == minpars or parlist == maxpars:
                    parlist[:] = [0, 0, 0, 0] * npeaks

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

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

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

            return f

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

        parnames = {0: "AMPLITUDE", 1: "SHIFT", 2: "GFWHM", 3: "LFWHM"}

        parinfo = [{
            'n': ii,
            'value': params[ii],
            'limits': [minpars[ii], maxpars[ii]],
            'limited': [limitedmin[ii], limitedmax[ii]],
            'fixed': fixed[ii],
            'parname': parnames[ii % 4] + str(ii / 4),
            '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
        if mp.perror is not None: mpperr = mp.perror
        else: mpperr = mpp * 0
        chi2 = mp.fnorm

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

        if not shh:
            print "Fit message ", mp.errmsg
            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)

        self.mp = mp
        self.mpp = mpp
        self.mpperr = mpperr
        self.model = self.n_voigt(pars=mpp)(xax)
        return mpp, self.n_voigt(pars=mpp)(xax), mpperr, chi2
Exemplo n.º 9
0
    def _baseline(self, spectrum, xarr=None, err=None,
                  order=1, quiet=True, mask=None, powerlaw=False,
                  xarr_fit_units='pixels', LoudDebug=False, renormalize='auto',
                  zeroerr_is_OK=True, spline=False, **kwargs):
        """
        Fit a baseline/continuum to a spectrum
        """

        #if xmin == 'default':
        #    if order <= 1 and mask is None: xmin = np.floor( spectrum.shape[-1]*0.1 )
        #    else: xmin = 0
        #elif xmin is None:
        #    xmin = 0
        #if xmax == 'default':
        #    if order <= 1 and mask is None: xmax = np.ceil( spectrum.shape[-1]*0.9 )
        #    else: xmax = spectrum.shape[-1]
        #elif xmax is None:
        #    xmax = spectrum.shape[-1]
        
        if xarr is None:
            xarr = np.indices(spectrum.shape).squeeze()


        # A good alternate implementation of masking is to only pass mpfit the data
        # that is unmasked.  That would require some manipulation above...
        if err is None:
            err = np.ones(spectrum.shape)
        else:
            # don't overwrite error
            err = err.copy()
            # assume anything with 0 error is GOOD
            if zeroerr_is_OK:
                err[err == 0] = 1.
            else: # flag it out!
                err[err == 0] = 1e10


        #err[:xmin] = 1e10
        #err[xmax:] = 1e10
        if mask is not None:
            if mask.dtype.name != 'bool': mask = mask.astype('bool')
            err[mask] = 1e10
            if LoudDebug: print "In _baseline: %i points masked out" % mask.sum()
        if (spectrum!=spectrum).sum() > 0:
            print "There is an error in baseline: some values are NaN"
            import pdb; pdb.set_trace()

        #xarrconv = xarr[xmin:xmax].as_unit(xarr_fit_units)
        OK = True-mask
        xarrconv = xarr.as_unit(xarr_fit_units)
        if powerlaw:
            # for powerlaw fitting, only consider positive data
            OK *= spectrum > 0
            pguess = [np.median(spectrum[OK]),2.0]
            if LoudDebug: print "_baseline powerlaw Guesses: ",pguess

            def mpfitfun(data,err):
                #def f(p,fjac=None): return [0,np.ravel(((p[0] * (xarrconv[OK]-p[2])**(-p[1]))-data)/err)]
                # Logarithmic fitting:
                def f(p,fjac=None):
                    #return [0,
                    #        np.ravel( (np.log10(data) - np.log10(p[0]) + p[1]*np.log10(xarrconv[OK]/p[2])) / (err/data) )
                    #        ]
                    return [0, np.ravel( (data - self.get_model(xarr=xarrconv[OK],baselinepars=p)) / (err/data) )]
                return f
        else:
            pguess = [0]*(order+1)
            if LoudDebug: print "_baseline Guesses: ",pguess

            def mpfitfun(data,err):
                def f(p,fjac=None): return [0,np.ravel((np.poly1d(p)(xarrconv[OK])-data)/err)]
                return f
        #scalefactor = 1.0
        #if renormalize in ('auto',True):
        #    datarange = spectrum.max() - spectrum.min()
        #    if abs(datarange) < 1e-9 or abs(datarange) > 1e9:
        #        scalefactor = np.median(np.abs(self.spectrum))
        #        print "BASELINE: Renormalizing data by factor %e to improve fitting procedure" % scalefactor
        #        spectrum /= scalefactor
        #        err /= scalefactor

        import pyspeckit.mpfit as mpfit
        mp = mpfit.mpfit(mpfitfun(spectrum[OK],err[OK]),xall=pguess,quiet=quiet) # mpfit doesn't need to take kwargs, I think ,**kwargs)
        if np.isnan(mp.fnorm):
            raise ValueError("chi^2 is NAN in baseline fitting")
        fitp = mp.params
        if powerlaw:
            #bestfit = (fitp[0]*(xarrconv)**(-fitp[1])).squeeze()
            bestfit = (fitp[0]*(xarrconv)**(-fitp[1])).squeeze()
        else:
            bestfit = np.poly1d(fitp)(xarrconv).squeeze()

        return bestfit,fitp
Exemplo n.º 10
0
    def fitter(self, xax, data, err=None, quiet=True, veryverbose=False,
            debug=False, parinfo=None, **kwargs):
        """
        Run the fitter using mpfit.
        
        kwargs will be passed to _make_parinfo and mpfit.

        Parameters
        ----------
        xax : SpectroscopicAxis 
            The X-axis of the spectrum
        data : ndarray
            The data to fit
        err : ndarray (optional)
            The error on the data.  If unspecified, will be uniform unity
        parinfo : ParinfoList
            The guesses, parameter limits, etc.  See
            `pyspeckit.spectrum.parinfo` for details
        quiet : bool
            pass to mpfit.  If False, will print out the parameter values for
            each iteration of the fitter
        veryverbose : bool
            print out a variety of mpfit output parameters
        debug : bool
            raise an exception (rather than a warning) if chi^2 is nan
        """

        if parinfo is None:
            parinfo, kwargs = self._make_parinfo(debug=debug, **kwargs)
        else:
            if debug: print "Using user-specified parinfo dict"
            # clean out disallowed kwargs (don't want to pass them to mpfit)
            #throwaway, kwargs = self._make_parinfo(debug=debug, **kwargs)

        self.xax = xax # the 'stored' xax is just a link to the original
        if hasattr(xax,'convert_to_unit') and self.fitunits is not None:
            # some models will depend on the input units.  For these, pass in an X-axis in those units
            # (gaussian, voigt, lorentz profiles should not depend on units.  Ammonia, formaldehyde,
            # H-alpha, etc. should)
            xax = copy.copy(xax)
            xax.convert_to_unit(self.fitunits, quiet=quiet)
        elif self.fitunits is not None:
            raise TypeError("X axis does not have a convert method")

        if np.any(np.isnan(data)) or np.any(np.isinf(data)):
            err[np.isnan(data) + np.isinf(data)] = np.inf
            data[np.isnan(data) + np.isinf(data)] = 0

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

        mp = mpfit(self.mpfitfun(xax,data,err),parinfo=parinfo,quiet=quiet,**kwargs)
        mpp = mp.params
        if mp.perror is not None: mpperr = mp.perror
        else: mpperr = mpp*0
        chi2 = mp.fnorm

        if mp.status == 0:
            if "parameters are not within PARINFO limits" in mp.errmsg:
                print parinfo
            raise mpfitException(mp.errmsg)

        for i,(p,e) in enumerate(zip(mpp,mpperr)):
            self.parinfo[i]['value'] = p
            self.parinfo[i]['error'] = e

        if veryverbose:
            print "Fit status: ",mp.status
            print "Fit error message: ",mp.errmsg
            print "Fit message: ",mpfit_messages[mp.status]
            for i,p in enumerate(mpp):
                print self.parinfo[i]['parname'],p," +/- ",mpperr[i]
            print "Chi2: ",mp.fnorm," Reduced Chi2: ",mp.fnorm/len(data)," DOF:",len(data)-len(mpp)

        self.mp = mp
        self.mpp = self.parinfo.values
        self.mpperr = self.parinfo.errors
        self.mppnames = self.parinfo.names
        self.model = self.n_modelfunc(self.parinfo,**self.modelfunc_kwargs)(xax)
        if debug:
            print "Modelpars: ",self.mpp
        if np.isnan(chi2):
            if debug:
                raise ValueError("Error: chi^2 is nan")
            else:
                print "Warning: chi^2 is nan"
        return mpp,self.model,mpperr,chi2
Exemplo n.º 11
0
    def multigaussfit(self, xax, data, npeaks=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, negamp=None,
            tied = ['', '', ''], parinfo=None, debug=False, **kwargs):
        """
        An improvement on onepeakgaussfit.  Lets you fit multiple gaussians.

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

         These parameters need to have length = 3*npeaks.  If npeaks > 1 and length = 3, they will
         be replicated npeaks times, otherwise they will be reset to defaults:
           params - Fit parameters: [amplitude, offset, width] * npeaks
                  If len(params) % 3 == 0, npeaks 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
           tied - link parameters together

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

           kwargs are passed to mpfit

        Returns:
           Fit parameters
           Model
           Fit errors
           chi2
        """
        
        if len(params) != npeaks and (len(params) / 3) > npeaks:
            self.npeaks = len(params) / 3 
        else:
            self.npeaks = npeaks

        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
        # multiformaldehydefit should process negamp directly if kwargs.has_key('negamp') is False: kwargs['negamp'] = None 
        pardict = {"params":params,"fixed":fixed,"limitedmin":limitedmin,"limitedmax":limitedmax,"minpars":minpars,"maxpars":maxpars,"tied":tied}
        for parlistname in pardict:
            parlist = pardict[parlistname]
            if len(parlist) != 3*self.npeaks:
                # if you leave the defaults, or enter something that can be multiplied by 3 to get to the
                # right number of formaldehydeians, it will just replicate
                if veryverbose: print "Correcting length of parameter %s" % parlistname
                if len(parlist) == 3: 
                    parlist *= self.npeaks 
                elif parlistname=="params":
                    parlist[:] = [1,0,1] * self.npeaks
                elif parlistname=="fixed":
                    parlist[:] = [False,False,False] * self.npeaks
                elif parlistname=="limitedmax":
                    if negamp is None: parlist[:] = [False,False,False] * self.npeaks
                    elif negamp is False: parlist[:] = [False,False,False] * self.npeaks
                    else: parlist[:] = [True,False,False] * self.npeaks
                elif parlistname=="limitedmin":
                    if negamp is None: parlist[:] = [False,False,True] * self.npeaks  # Lines can't have negative width!
                    elif negamp is False: parlist[:] = [True,False,True] * self.npeaks
                    else: parlist[:] = [False,False,True] * self.npeaks                   
                elif parlistname=="minpars" or parlistname=="maxpars":
                    parlist[:] = [0,0,0] * self.npeaks
                elif parlistname=="tied":
                    parlist[:] = ['','',''] * self.npeaks
                    
        # mpfit doesn't recognize negamp, so get rid of it now that we're done setting limitedmin/max and min/maxpars
        #if kwargs.has_key('negamp'): kwargs.pop('negamp')

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

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

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

        if parinfo is None:
            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, 'tied':tied[ii]} 
                for ii in xrange(len(params)) ]

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

        if debug: 
            for p in parinfo: print p
            
        mp = mpfit(mpfitfun(xax,data,err),parinfo=parinfo,quiet=quiet,**kwargs)
        mpp = mp.params
        if mp.perror is not None: mpperr = mp.perror
        else: mpperr = mpp*0
        chi2 = mp.fnorm

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

        if not shh:
            print "Fit status: ",mp.status
            print "Fit error message: ",mp.errmsg
            print "Fit message: ",mpfit_messages[mp.status]
            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)

        self.mp = mp
        self.mpp = mpp
        self.mpperr = mpperr
        self.model = self.n_gaussian(pars=mpp)(xax)
        return mpp,self.n_gaussian(pars=mpp)(xax),mpperr,chi2
Exemplo n.º 12
0
# Generate model data for a Gaussian with param mu and sigma and add noise
import warnings
warnings.filterwarnings('once')
x= SlambdaWave
preal=[1,0.22,2]
y_true=peval(x,preal)
mu,sigma=0,1
y      = y_true + 0.01 * np.random.normal(mu,sigma, len(y_true))
err    = 0.1 * np.random.normal(mu,sigma, len(y_true) )
# Initial estimates for MPFIT
p0 = [1.2, 0.2,1.8]
fa = {'x':x, 'y':y, 'err':err}

# Call MPFIT with user defined function 'myfunct'
m = mpfit.mpfit(myfunct, p0, functkw=fa )

print("status: ", m.status)
if (m.status <= 0): 
   print('error message = ', m.errmsg)
else:
   print("Iterations: ", m.niter)
   print("Fitted pars: ", m.params)
   print("Uncertainties: ", m.perror )


# In[119]:


p = preal
SpectrumFlux = f11Fun(x) + SlambdaFun(x)*(p[0]-1.1)
Exemplo n.º 13
0
def emtau(freq,
          flux,
          err=None,
          EMguess=1e7 * emu,
          Te=default_te,
          normfac=5e-6,
          quiet=1,
          dust=False,
          dustT=False,
          alpha=3.5,
          normfac2=1e-6,
          beta=1.75,
          use_mpfit=False,
          maxiter=500,
          **kwargs):
    """
    Returns emission measure & optical depth given radio continuum data points
    at frequency freq with flux density flux.

    return bestEM,nu(tau=1),chi^2
    """
    EMguess = with_unit(EMguess, emu)
    Te = with_unit(Te, u.K)
    flux = with_unit(flux, u.Jy)
    err = with_unit(err, u.Jy)

    ok = np.isfinite(freq) & np.isfinite(flux) & np.isfinite(err)

    guesses = [(EMguess / emu).decompose().value, normfac]
    if dust:
        guesses += [alpha, normfac2]
    elif dustT:
        guesses += [beta, normfac2, dustT.to(u.K).value]

    if use_mpfit:
        mp = mpfit.mpfit(mpfitfun(freq[ok],
                                  flux[ok],
                                  err[ok],
                                  dust=dust,
                                  dustT=bool(dustT)),
                         xall=guesses,
                         maxiter=maxiter,
                         quiet=quiet)
        mpp = mp.params
        mpperr = mp.perror
        chi2 = mp.fnorm
        bestEM = mpp[0]
        normfac = mpp[1]
    else:
        fitter = LevMarLSQFitter()
        model = (inufit_dustT
                 if bool(dustT) else inufit_dust if dust else inufit)
        m_init = model(*guesses)

        m_init.Te.fixed = True
        m_init.nu0.fixed = True

        m_init.em.min = 1  # cannot be less than 1 cm^-6 pc
        m_init.normfac.min = 0
        if hasattr(m_init, 'beta'):
            m_init.beta.min = 1
            m_init.beta.max = 10
            m_init.normfac2.min = 0
        elif hasattr(m_init, 'alpha'):
            m_init.alpha.min = 0
            m_init.alpha.max = 12

        fitted = fitter(m_init,
                        freq[ok].to(u.GHz).value,
                        flux[ok].to(u.mJy).value,
                        weights=1 / err[ok].to(u.mJy).value,
                        maxiter=maxiter)
        mp = fitter
        mp.params = fitted.parameters
        mpp = fitted.parameters
        try:
            assert 'cov_x' in fitter.fit_info and fitter.fit_info[
                'cov_x'] is not None
            mpperr = fitter.fit_info['cov_x'].diagonal()**0.5
        except AssertionError:
            mpperr = [np.nan] * len(guesses)
        chi2 = (((u.Quantity(fitted(freq[ok]), u.mJy) - flux[ok]) / err[ok])**
                2).sum() / (ok.sum() - len(guesses))
        bestEM = mpp[0]
        normfac = mpp[1]
        log.info(fitter.fit_info['message'])

    # bestEM is unitless w/ emu units
    nu_tau = (((Te / u.K)**1.35 / (bestEM) / 8.235e-2)**(-1 / 2.1)).decompose()

    return with_unit(bestEM, emu), nu_tau, normfac, chi2, mp
Exemplo n.º 14
0
    def onedvoigtfit(self,xax, data, err=None,
            params=[0,1,0,1,1],fixed=[False,False,False,False,False],
            limitedmin=[False,False,False,True,True],
            limitedmax=[False,False,False,False,False], minpars=[0,0,0,0,0],
            maxpars=[0,0,0,0,0], quiet=True, shh=True, veryverbose=False,
            vheight=True, negamp=False, usemoments=False, **kwargs):
        """
        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-self.n_voigt(pars=p[1:])(x)+p[0])]
            else:
                def f(p,fjac=None): return [0,(y-self.n_voigt(pars=p[1:])(x)+p[0])/err]
            return f

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

        if vheight is False: 
            height = params[0]
            fixed[0] = True
        if usemoments:
            params = moments(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':"GWIDTH",'error':0},
                    {'n':4,'value':params[4],'limits':[minpars[4],maxpars[4]],'limited':[limitedmin[4],limitedmax[4]],'fixed':fixed[4],'parname':"LWIDTH",'error':0}]

        mp = mpfit(mpfitfun(xax,data,err),parinfo=parinfo,quiet=quiet)
        mpp = mp.params
        if mp.perror is not None: mpperr = mp.perror
        else: mpperr = mpp*0
        chi2 = mp.fnorm

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

        if (not shh) or veryverbose:
            print "Fit message ",mp.errmsg
            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)

        self.mp = mp
        self.mpp = mpp[1:]
        self.mpperr = mpperr
        self.model = self.n_voigt(pars=mpp[1:])(xax) 
        return mpp,self.n_voigt(pars=mpp[1:])(xax),mpperr,chi2
Exemplo n.º 15
0
    def multinh3fit(self, xax, data, npeaks=1, err=None, 
            params=(20,20,14,1.0,0.0,0.5),
            parnames=None,
            fixed=(False,False,False,False,False,False),
            limitedmin=(True,True,True,True,False,True),
            limitedmax=(False,False,False,False,False,True), minpars=(2.73,2.73,0,0,0,0),
            parinfo=None,
            maxpars=(0,0,0,0,0,1), quiet=True, shh=True, veryverbose=False, **kwargs):
        """
        Fit multiple nh3 profiles (multiple can be 1)

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

         These parameters need to have length = 6*npeaks.  If npeaks > 1 and length = 6, they will
         be replicated npeaks times, otherwise they will be reset to defaults:
           params - Fit parameters: [tkin, tex, ntot (or tau), width, offset, ortho fraction] * npeaks
                  If len(params) % 6 == 0, npeaks will be set to len(params) / 6
           fixed - Is parameter fixed?
           limitedmin/minpars - set lower limits on each parameter (default: width>0, Tex and Tkin > Tcmb)
           limitedmax/maxpars - set upper limits on each parameter
           parnames - default parameter names, important for setting kwargs in model ['tkin','tex','ntot','width','xoff_v','fortho']

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

        Returns:
           Fit parameters
           Model
           Fit errors
           chi2
        """

        if parinfo is None:
            self.npars = len(params) / npeaks

            if len(params) != npeaks and (len(params) / self.npars) > npeaks:
                npeaks = len(params) / self.npars 
            self.npeaks = npeaks

            if isinstance(params,np.ndarray): params=params.tolist()
            # this is actually a hack, even though it's decently elegant
            # somehow, parnames was being changed WITHOUT being passed as a variable
            # this doesn't make sense - at all - but it happened.
            # (it is possible for self.parnames to have npars*npeaks elements where
            # npeaks > 1 coming into this function even though only 6 pars are specified;
            # _default_parnames is the workaround)
            if parnames is None: parnames = copy.copy(self._default_parnames)

            partype_dict = dict(zip(['params','parnames','fixed','limitedmin','limitedmax','minpars','maxpars'],
                    [params,parnames,fixed,limitedmin,limitedmax,minpars,maxpars]))

            # make sure all various things are the right length; if they're not, fix them using the defaults
            for partype,parlist in partype_dict.iteritems():
                if len(parlist) != self.npars*self.npeaks:
                    # if you leave the defaults, or enter something that can be multiplied by npars to get to the
                    # right number of gaussians, it will just replicate
                    if len(parlist) == self.npars: 
                        partype_dict[partype] *= npeaks 
                    elif len(parlist) > self.npars:
                        # DANGER:  THIS SHOULD NOT HAPPEN!
                        print "WARNING!  Input parameters were longer than allowed for variable ",parlist
                        partype_dict[partype] = partype_dict[partype][:self.npars]
                    elif parlist==params: # this instance shouldn't really be possible
                        partype_dict[partype] = [20,20,1e10,1.0,0.0,0.5] * npeaks
                    elif parlist==fixed:
                        partype_dict[partype] = [False] * len(params)
                    elif parlist==limitedmax: # only fortho, fillingfraction have upper limits
                        partype_dict[partype] = (np.array(parnames) == 'fortho') + (np.array(parnames) == 'fillingfraction')
                    elif parlist==limitedmin: # no physical values can be negative except velocity
                        partype_dict[partype] = (np.array(parnames) != 'xoff_v')
                    elif parlist==minpars: # all have minima of zero except kinetic temperature, which can't be below CMB.  Excitation temperature technically can be, but not in this model
                        partype_dict[partype] = ((np.array(parnames) == 'tkin') + (np.array(parnames) == 'tex')) * 2.73
                    elif parlist==maxpars: # fractions have upper limits of 1.0
                        partype_dict[partype] = ((np.array(parnames) == 'fortho') + (np.array(parnames) == 'fillingfraction')).astype('float')
                    elif parlist==parnames: # assumes the right number of parnames (essential)
                        partype_dict[partype] = list(parnames) * self.npeaks 

            if len(parnames) != len(partype_dict['params']):
                raise ValueError("Wrong array lengths AFTER fixing them")

            # used in components.  Is this just a hack?
            self.parnames = partype_dict['parnames']

            parinfo = [ {'n':ii, 'value':partype_dict['params'][ii],
                'limits':[partype_dict['minpars'][ii],partype_dict['maxpars'][ii]],
                'limited':[partype_dict['limitedmin'][ii],partype_dict['limitedmax'][ii]], 'fixed':partype_dict['fixed'][ii],
                'parname':partype_dict['parnames'][ii]+str(ii/self.npars),
                'mpmaxstep':float(partype_dict['parnames'][ii] in ('tex','tkin')), # must force small steps in temperature (True = 1.0)
                'error': 0} 
                for ii in xrange(len(partype_dict['params'])) ]

            # hack: remove 'fixed' pars
            parinfo_with_fixed = parinfo
            parinfo = [p for p in parinfo_with_fixed if not p['fixed']]
            fixed_kwargs = dict((p['parname'].strip("0123456789").lower(),p['value']) for p in parinfo_with_fixed if p['fixed'])
            # don't do this - it breaks the NEXT call because npars != len(parnames) self.parnames = [p['parname'] for p in parinfo]
            # this is OK - not a permanent change
            parnames = [p['parname'] for p in parinfo]
            # not OK self.npars = len(parinfo)/self.npeaks
            parinfo = ParinfoList([Parinfo(p) for p in parinfo], preserve_order=True)
            #import pdb; pdb.set_trace()
        else: 
            self.parinfo = ParinfoList([Parinfo(p) for p in parinfo], preserve_order=True) 
            parinfo_with_fixed = None
            fixed_kwargs = {}

        fitfun_kwargs = dict(kwargs.items()+fixed_kwargs.items())

        npars = len(parinfo)/self.npeaks

        # (fortho0 is not fortho)
        # this doesn't work if parinfo_with_fixed is not None:
        # this doesn't work     for p in parinfo_with_fixed:
        # this doesn't work         # users can change the defaults while holding them fixed 
        # this doesn't work         if p['fixed']:
        # this doesn't work             kwargs.update({p['parname']:p['value']})

        def mpfitfun(x,y,err):
            if err is None:
                def f(p,fjac=None): return [0,(y-self.n_ammonia(pars=p, parnames=parinfo.parnames, **fitfun_kwargs)(x))]
            else:
                def f(p,fjac=None): return [0,(y-self.n_ammonia(pars=p, parnames=parinfo.parnames, **fitfun_kwargs)(x))/err]
            return f

        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
        if mp.perror is not None: mpperr = mp.perror
        else: mpperr = mpp*0
        chi2 = mp.fnorm

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

        for i,p in enumerate(mpp):
            parinfo[i]['value'] = p
            parinfo[i]['error'] = mpperr[i]

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

        if any(['tex' in s for s in parnames]) and any(['tkin' in s for s in parnames]):
            texnum = (i for i,s in enumerate(parnames) if 'tex' in s)
            tkinnum = (i for i,s in enumerate(parnames) if 'tkin' in s)
            for txn,tkn in zip(texnum,tkinnum):
                if mpp[txn] > mpp[tkn]: mpp[txn] = mpp[tkn]  # force Tex>Tkin to Tex=Tkin (already done in n_ammonia)
        self.mp = mp

        if parinfo_with_fixed is not None:
            # self self.parinfo preserving the 'fixed' parameters 
            # ORDER MATTERS!
            for p in parinfo:
                parinfo_with_fixed[p['n']] = p
            self.parinfo = ParinfoList([Parinfo(p) for p in parinfo_with_fixed], preserve_order=True)
        else:
            self.parinfo = parinfo
            self.parinfo = ParinfoList([Parinfo(p) for p in parinfo], preserve_order=True)

        # I don't THINK these are necessary?
        #self.parinfo = parinfo
        #self.parinfo = ParinfoList([Parinfo(p) for p in self.parinfo])

        # need to restore the fixed parameters....
        # though the above commented out section indicates that I've done and undone this dozens of times now
        # (a test has been added to test_nh3.py)
        # this was NEVER included or tested because it breaks the order
        #for par in parinfo_with_fixed:
        #    if par.parname not in self.parinfo.keys():
        #        self.parinfo.append(par)

        self.mpp = self.parinfo.values
        self.mpperr = self.parinfo.errors
        self.mppnames = self.parinfo.names
        self.model = self.n_ammonia(pars=self.mpp, parnames=self.mppnames, **kwargs)(xax)
        #if self.model.sum() == 0:
        #    print "DON'T FORGET TO REMOVE THIS ERROR!"
        #    raise ValueError("Model is zeros.")

        indiv_parinfo = [self.parinfo[jj*self.npars:(jj+1)*self.npars] for jj in xrange(len(self.parinfo)/self.npars)]
        modelkwargs = [
                dict([(p['parname'].strip("0123456789").lower(),p['value']) for p in pi])
                for pi in indiv_parinfo]
        self.tau_list = [ammonia(xax,return_tau=True,**mk) for mk in modelkwargs]

        return self.mpp,self.model,self.mpperr,chi2
Exemplo n.º 16
0
    def multinh3fit(self,
                    xax,
                    data,
                    err=None,
                    parinfo=None,
                    quiet=True,
                    shh=True,
                    debug=False,
                    maxiter=200,
                    use_lmfit=False,
                    veryverbose=False,
                    **kwargs):
        """
        Fit multiple nh3 profiles (multiple can be 1)

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

         These parameters need to have length = 6*npeaks.  If npeaks > 1 and length = 6, they will
         be replicated npeaks times, otherwise they will be reset to defaults:
           params - Fit parameters: [tkin, tex, ntot (or tau), width, offset, ortho fraction] * npeaks
                  If len(params) % 6 == 0, npeaks will be set to len(params) / 6
           fixed - Is parameter fixed?
           limitedmin/minpars - set lower limits on each parameter (default: width>0, Tex and Tkin > Tcmb)
           limitedmax/maxpars - set upper limits on each parameter
           parnames - default parameter names, important for setting kwargs in model ['tkin','tex','ntot','width','xoff_v','fortho']

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

        Returns:
           Fit parameters
           Model
           Fit errors
           chi2
        """

        if parinfo is None:
            parinfo = self.parinfo = self.make_parinfo(**kwargs)
        else:
            if isinstance(parinfo, ParinfoList):
                if not quiet:
                    log.info("Using user-specified parinfo.")
                self.parinfo = parinfo
            else:
                if not quiet:
                    log.info(
                        "Using something like a user-specified parinfo, but not."
                    )
                self.parinfo = ParinfoList([
                    p if isinstance(p, Parinfo) else Parinfo(p)
                    for p in parinfo
                ],
                                           preserve_order=True)

        fitfun_kwargs = dict(
            (x, y) for (x, y) in kwargs.items()
            if x not in ('npeaks', 'params', 'parnames', 'fixed', 'limitedmin',
                         'limitedmax', 'minpars', 'maxpars', 'tied',
                         'max_tem_step'))
        if 'use_lmfit' in fitfun_kwargs:
            raise KeyError("use_lmfit was specified in a location where it "
                           "is unacceptable")

        npars = len(parinfo) / self.npeaks

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

                def f(p, fjac=None):
                    return [
                        0,
                        (y - self.n_ammonia(pars=p,
                                            parnames=parinfo.parnames,
                                            **fitfun_kwargs)(x))
                    ]
            else:

                def f(p, fjac=None):
                    return [
                        0,
                        (y - self.n_ammonia(
                            pars=p, parnames=parinfo.parnames, **fitfun_kwargs)
                         (x)) / err
                    ]

            return f

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

        if use_lmfit:
            return self.lmfitter(xax,
                                 data,
                                 err=err,
                                 parinfo=parinfo,
                                 quiet=quiet,
                                 debug=debug)
        else:
            mp = mpfit(mpfitfun(xax, data, err),
                       parinfo=parinfo,
                       maxiter=maxiter,
                       quiet=quiet,
                       debug=debug)
            mpp = mp.params
            if mp.perror is not None: mpperr = mp.perror
            else: mpperr = mpp * 0
            chi2 = mp.fnorm

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

        for i, p in enumerate(mpp):
            parinfo[i]['value'] = p
            parinfo[i]['error'] = mpperr[i]

        if not shh:
            log.info("Fit status: {0}".format(mp.status))
            log.info("Fit message: {0}".format(mpfit_messages[mp.status]))
            log.info("Fit error message: {0}".format(mp.errmsg))
            log.info("Final fit values: ")
            for i, p in enumerate(mpp):
                log.info(" ".join(
                    (parinfo[i]['parname'], str(p), " +/- ", str(mpperr[i]))))
            log.info(" ".join(("Chi2: ", str(mp.fnorm), " Reduced Chi2: ",
                               str(mp.fnorm / len(data)), " DOF:",
                               str(len(data) - len(mpp)))))

        self.mp = mp

        self.parinfo = parinfo

        self.mpp = self.parinfo.values
        self.mpperr = self.parinfo.errors
        self.mppnames = self.parinfo.names
        self.model = self.n_ammonia(pars=self.mpp,
                                    parnames=self.mppnames,
                                    **fitfun_kwargs)(xax)

        indiv_parinfo = [
            self.parinfo[jj * self.npars:(jj + 1) * self.npars]
            for jj in xrange(len(self.parinfo) / self.npars)
        ]
        modelkwargs = [
            dict([(p['parname'].strip("0123456789").lower(), p['value'])
                  for p in pi]) for pi in indiv_parinfo
        ]
        self.tau_list = [
            ammonia(xax, return_tau=True, **mk) for mk in modelkwargs
        ]

        return self.mpp, self.model, self.mpperr, chi2
Exemplo n.º 17
0
    def fitter(self, xax, data, err=None, quiet=True, veryverbose=False,
               debug=False, parinfo=None, **kwargs):
        """
        Run the fitter using mpfit.
        
        kwargs will be passed to _make_parinfo and mpfit.

        Parameters
        ----------
        xax : SpectroscopicAxis 
            The X-axis of the spectrum
        data : ndarray
            The data to fit
        err : ndarray (optional)
            The error on the data.  If unspecified, will be uniform unity
        parinfo : ParinfoList
            The guesses, parameter limits, etc.  See
            `pyspeckit.spectrum.parinfo` for details
        quiet : bool
            pass to mpfit.  If False, will print out the parameter values for
            each iteration of the fitter
        veryverbose : bool
            print out a variety of mpfit output parameters
        debug : bool
            raise an exception (rather than a warning) if chi^2 is nan
        """

        if parinfo is None:
            parinfo, kwargs = self._make_parinfo(debug=debug, **kwargs)
        else:
            log.debug("Using user-specified parinfo dict")
            # clean out disallowed kwargs (don't want to pass them to mpfit)
            #throwaway, kwargs = self._make_parinfo(debug=debug, **kwargs)

        self.xax = xax # the 'stored' xax is just a link to the original
        if hasattr(xax,'as_unit') and self.fitunits is not None:
            # some models will depend on the input units.  For these, pass in an X-axis in those units
            # (gaussian, voigt, lorentz profiles should not depend on units.  Ammonia, formaldehyde,
            # H-alpha, etc. should)
            xax = copy.copy(xax)
            # xax.convert_to_unit(self.fitunits, quiet=quiet)
            xax = xax.as_unit(self.fitunits, quiet=quiet, **kwargs)
        elif self.fitunits is not None:
            raise TypeError("X axis does not have a convert method")

        if np.any(np.isnan(data)) or np.any(np.isinf(data)):
            err[np.isnan(data) + np.isinf(data)] = np.inf
            data[np.isnan(data) + np.isinf(data)] = 0

        if np.any(np.isnan(err)):
            raise ValueError("One or more of the error values is NaN."
                             "  This is not allowed.  Errors can be infinite "
                             "(which is equivalent to giving zero weight to "
                             "a data point), but otherwise they must be positive "
                             "floats.")
        elif np.any(err<0):
            raise ValueError("At least one error value is negative, which is "
                             "not allowed as negative errors are not "
                             "meaningful in the optimization process.")

        for p in parinfo: log.debug( p )
        log.debug( "\n".join(["%s %i: tied: %s value: %s" % (p['parname'],p['n'],p['tied'],p['value']) for p in parinfo]) )

        mp = mpfit(self.mpfitfun(xax,data,err),parinfo=parinfo,quiet=quiet,debug=debug,**kwargs)
        mpp = mp.params
        if mp.perror is not None: mpperr = mp.perror
        else: mpperr = mpp*0
        chi2 = mp.fnorm

        if mp.status == 0:
            if "parameters are not within PARINFO limits" in mp.errmsg:
                log.warning( parinfo )
            raise mpfitException(mp.errmsg)

        for i,(p,e) in enumerate(zip(mpp,mpperr)):
            self.parinfo[i]['value'] = p
            self.parinfo[i]['error'] = e

        if veryverbose:
            log.info("Fit status: {0}".format(mp.status))
            log.info("Fit error message: {0}".format(mp.errmsg))
            log.info("Fit message: {0}".format(mpfit_messages[mp.status]))
            for i,p in enumerate(mpp):
                log.info("{0}: {1} +/- {2}".format(self.parinfo[i]['parname'],
                                                    p,mpperr[i]))
            log.info("Chi2: {0} Reduced Chi2: {1}  DOF:{2}".format(mp.fnorm,
                                                                   mp.fnorm/(len(data)-len(mpp)),
                                                                   len(data)-len(mpp)))

        self.mp = mp
        self.mpp = self.parinfo.values
        self.mpperr = self.parinfo.errors
        self.mppnames = self.parinfo.names
        self.model = self.n_modelfunc(self.parinfo,**self.modelfunc_kwargs)(xax)
        log.debug("Modelpars: {0}".format(self.mpp))
        if np.isnan(chi2):
            if debug:
                raise ValueError("Error: chi^2 is nan")
            else:
                log.warning("Warning: chi^2 is nan")
        return mpp,self.model,mpperr,chi2
Exemplo n.º 18
0
    def multigaussfit(self,
                      xax,
                      data,
                      npeaks=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,
                      negamp=None,
                      tied=['', '', ''],
                      parinfo=None,
                      debug=False,
                      **kwargs):
        """
        An improvement on onepeakgaussfit.  Lets you fit multiple gaussians.

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

         These parameters need to have length = 3*npeaks.  If npeaks > 1 and length = 3, they will
         be replicated npeaks times, otherwise they will be reset to defaults:
           params - Fit parameters: [amplitude, offset, width] * npeaks
                  If len(params) % 3 == 0, npeaks 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
           tied - link parameters together

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

           kwargs are passed to mpfit

        Returns:
           Fit parameters
           Model
           Fit errors
           chi2
        """

        if len(params) != npeaks and (len(params) / 3) > npeaks:
            self.npeaks = len(params) / 3
        else:
            self.npeaks = npeaks

        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
        # multiformaldehydefit should process negamp directly if kwargs.has_key('negamp') is False: kwargs['negamp'] = None
        pardict = {
            "params": params,
            "fixed": fixed,
            "limitedmin": limitedmin,
            "limitedmax": limitedmax,
            "minpars": minpars,
            "maxpars": maxpars,
            "tied": tied
        }
        for parlistname in pardict:
            parlist = pardict[parlistname]
            if len(parlist) != 3 * self.npeaks:
                # if you leave the defaults, or enter something that can be multiplied by 3 to get to the
                # right number of formaldehydeians, it will just replicate
                if veryverbose:
                    print("Correcting length of parameter %s" % parlistname)
                if len(parlist) == 3:
                    parlist *= self.npeaks
                elif parlistname == "params":
                    parlist[:] = [1, 0, 1] * self.npeaks
                elif parlistname == "fixed":
                    parlist[:] = [False, False, False] * self.npeaks
                elif parlistname == "limitedmax":
                    if negamp is None:
                        parlist[:] = [False, False, False] * self.npeaks
                    elif negamp is False:
                        parlist[:] = [False, False, False] * self.npeaks
                    else:
                        parlist[:] = [True, False, False] * self.npeaks
                elif parlistname == "limitedmin":
                    if negamp is None:
                        parlist[:] = [
                            False, False, True
                        ] * self.npeaks  # Lines can't have negative width!
                    elif negamp is False:
                        parlist[:] = [True, False, True] * self.npeaks
                    else:
                        parlist[:] = [False, False, True] * self.npeaks
                elif parlistname == "minpars" or parlistname == "maxpars":
                    parlist[:] = [0, 0, 0] * self.npeaks
                elif parlistname == "tied":
                    parlist[:] = ['', '', ''] * self.npeaks

        # mpfit doesn't recognize negamp, so get rid of it now that we're done setting limitedmin/max and min/maxpars
        #if kwargs.has_key('negamp'): kwargs.pop('negamp')

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

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

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

            return f

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

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

        if parinfo is None:
            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,
                'tied': tied[ii]
            } for ii in xrange(len(params))]

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

        if debug:
            for p in parinfo:
                print(p)

        mp = mpfit(mpfitfun(xax, data, err),
                   parinfo=parinfo,
                   quiet=quiet,
                   **kwargs)
        mpp = mp.params
        if mp.perror is not None: mpperr = mp.perror
        else: mpperr = mpp * 0
        chi2 = mp.fnorm

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

        if not shh:
            print("Fit status: ", mp.status)
            print("Fit error message: ", mp.errmsg)
            print("Fit message: ", mpfit_messages[mp.status])
            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))

        self.mp = mp
        self.mpp = mpp
        self.mpperr = mpperr
        self.model = self.n_gaussian(pars=mpp)(xax)
        return mpp, self.n_gaussian(pars=mpp)(xax), mpperr, chi2
Exemplo n.º 19
0
    def onedvoigtfit(self,
                     xax,
                     data,
                     err=None,
                     params=[0, 1, 0, 1, 1],
                     fixed=[False, False, False, False, False],
                     limitedmin=[False, False, False, True, True],
                     limitedmax=[False, False, False, False, False],
                     minpars=[0, 0, 0, 0, 0],
                     maxpars=[0, 0, 0, 0, 0],
                     quiet=True,
                     shh=True,
                     veryverbose=False,
                     vheight=True,
                     negamp=False,
                     usemoments=False,
                     **kwargs):
        """
        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 - self.n_voigt(pars=p[1:])(x) + p[0])]
            else:

                def f(p, fjac=None):
                    return [0, (y - self.n_voigt(pars=p[1:])(x) + p[0]) / err]

            return f

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

        if vheight is False:
            height = params[0]
            fixed[0] = True
        if usemoments:
            params = moments(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': "GWIDTH",
            'error': 0
        }, {
            'n': 4,
            'value': params[4],
            'limits': [minpars[4], maxpars[4]],
            'limited': [limitedmin[4], limitedmax[4]],
            'fixed': fixed[4],
            'parname': "LWIDTH",
            'error': 0
        }]

        mp = mpfit(mpfitfun(xax, data, err), parinfo=parinfo, quiet=quiet)
        mpp = mp.params
        if mp.perror is not None: mpperr = mp.perror
        else: mpperr = mpp * 0
        chi2 = mp.fnorm

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

        if (not shh) or veryverbose:
            print "Fit message ", mp.errmsg
            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)

        self.mp = mp
        self.mpp = mpp[1:]
        self.mpperr = mpperr
        self.model = self.n_voigt(pars=mpp[1:])(xax)
        return mpp, self.n_voigt(pars=mpp[1:])(xax), mpperr, chi2
Exemplo n.º 20
0
for i in xrange(
        0,
        nparams):  # To initialize all the individual dictionaries separately
    dictionary['data'] = p3[
        i]  # We give to each dictionary the initial values for each parameter
    parinf.append(dictionary.copy())

parinf[6]['tied'] = p3[
    3]  # Put constraint that the sigma should be the same for both gaussians
parinf[5]['tied'] = p3[2] * (
    l_SII_1 / l_SII_2
)  # Put a constraint on the differences between wavelengths of the lines

# Make the fit using mpfit for one gaussian and for the combination of as many gaussians as wanted with
# a linear fit for the continuum
m = mpfit.mpfit(usefunctions.gausfunct, p0, functkw=fa)
m3 = mpfit.mpfit(usefunctions.gaus3funct, p3, functkw=fa3, parinfo=parinf)

# Calculate the residuals of the data
resid = newy1 - usefunctions.gaussian(newx1, m.params)

# In order to determine if the lines need one more gaussian to be fit correctly, we apply the condition
# that the std dev of the continuum should be higher than 3 times the std dev of the residuals of the
# fit of the line. We have to calculate the stddev of the continuum in a place where there are no
# lines (True for all AGNs spectra in this range).
# Calculate the standard deviation of a part of the continuum without lines nor contribution of them
std0 = np.where(l > 6450.)[0][0]
std1 = np.where(l < 6500.)[0][-1]
stadev = np.std(data_cor[std0:std1])

#############################################################################################################