def LPfit(v,i,init=dict(Te=50, Vf=15, I0=None), plot=None, maxits=None):
    #Takes about 2ms/it for 2500 points, and halving this only saves 10%
    if maxits is None:  # this trickiness makes it work, but not pythonic.
        maxits = globals()['maxits']
    if plot is None: plot = debug>0

    Te = init['Te']
    Vf = init['Vf']
    I0 = init['I0']
    if I0 is None:
        I0 = 1.7 * np.average(np.clip(i, 0, 1000))

    var = [Te, Vf, I0]
    scale = np.array([Te, Te/3, I0])
    fit_results = amoeba.amoeba(var, scale, error_fun, itmax = maxits, 
                                data = dict(v=v, i=i))
    Te, Vf, I0 = fit_results[0]
    plotchar(v, Te, Vf, I0, linewidth=4)
    residual, mits = fit_results[1:3]
    if plot: 
        plt.title('Te = {Te:.1f}eV, Vf = {Vf:.1f}, Isat = {I0:.3g}, resid = {r:.2g} {mits} its '
                       .format(Te=Te, Vf=Vf, I0=I0, r=-1*float(residual), mits=mits))
        plt.suptitle('{shot} {tr} {d}'
                     .format(tr=t_range, shot=shot_number, d=diag_name))
        plt.show(block=0)

    return(fit_results)
def LPfit(v,i,init=dict(Te=50, Vf=15, I0=None), plot=None, maxits=None):
    #Takes about 2ms/it for 2500 points, and halving this only saves 10%
    if maxits is None:  # this trickiness makes it work, but not pythonic.
        maxits = globals()['maxits']
    if plot is None: plot = debug>0

    Te = init['Te']
    Vf = init['Vf']
    I0 = init['I0']
    if I0 is None:
        I0 = 1.7 * np.average(np.clip(i, 0, 1000))

    var = [Te, Vf, I0]
    scale = np.array([Te, Te/3, I0])
    fit_results = amoeba.amoeba(var, scale, error_fun, itmax = maxits, 
                                data = dict(v=v, i=i))
    Te, Vf, I0 = fit_results[0]
    plotchar(v, Te, Vf, I0, linewidth=4)
    residual, mits = fit_results[1:3]
    if plot: 
        plt.title('Te = {Te:.1f}eV, Vf = {Vf:.1f}, Isat = {I0:.3g}, resid = {r:.2e} {mits} its '
                       .format(Te=Te, Vf=Vf, I0=I0, r=-residual, mits=mits))
        plt.suptitle('{shot} {tr} {d}'
                     .format(tr=t_range, shot=shot_number, d=diag_name))
        plt.show(block=0)

    return(fit_results)
    def fit(self, init=None, plot=None, fit_params=None, default_init=dict(Te=50, Vf=15, I0=None)):
        """ Perform a curve fit operation with the given parameter and initial
        value.  'None' invokes the fit_params determined at creation
        of the class, and for 'init', the default_init

        """
        # Takes about 2ms/it for 2500 points, and halving this only saves 10%
        # if maxits is None:  # this trickiness makes it work, but not pythonic.
        #    maxits = globals()['maxits']
        if init is None:
            init = default_init
        else: 
            default_init.update(init)
            init = default_init

        self.trace = []
        if plot is None:
            plot = self.plot

        # if None, get the defaults from the __init__
        if fit_params is None:
            if self.fit_params is not None:
                fit_params = self.fit_params
            else:
                raise ValueError('Need fit params in LPfitter class or call to .fit')
        self.actual_fparams = self.fit_params
        self.actual_fparams.update(fit_params)

        alg = self.actual_fparams['alg']
        maxits = self.actual_fparams['maxits']
        ftol = self.actual_fparams['ftol']
        xtol = self.actual_fparams['xtol']
        Lnorm = self.actual_fparams['Lnorm']

        Te = init['Te']
        Vf = init['Vf']
        I0 = init['I0']
        if I0 is None:
            I0 = 1.7 * np.average(np.clip(self.i, 0, 1000))

        var = [Te, Vf, I0]
        scale = np.array([Te, Te/3, I0])
        if alg == 'leastsq':
            pfit, pcov, infodict, msg, ier = \
                leastsq(self.residuals, var, args=(self.v, self.i), maxfev=maxits,
                        full_output=1, diag=scale, ftol=ftol*I0, xtol=xtol)
            # residual already raised to power Lnorm/2 = so just square here
            resid = np.power(np.mean(np.abs(infodict['fvec']**2)),1./Lnorm)
            # this is required to scale the covariance
            s_sq = (self.residuals(pfit, self.v, self.i)**2).sum()/(len(self.i)-len(init))
            fit_results = [pfit, resid, infodict['nfev']]
            if ier not in [1,2,3,4]: 
                fit_results[-1] = 9000+ier  # keep it in uint16 range
            if self.parent is not None and self.parent.debug>0:
                if self.parent.debug>1 or ier not in [1,2,3,4]:
                    print(ier, msg)

        elif alg == 'amoeba':
            fit_results = list(amoeba.amoeba(var, scale, self.error_fun, 
                                             itmax=maxits, ftolerance=ftol*I0,
                                             xtolerance=xtol,
                                             data=dict(v=self.v, i=self.i)))
            fit_results[1] = -fit_results[1]  # change residual back to positive
            s_sq = None  # don't know how to get cov for amoeba
            pcov = None

        Te, Vf, I0 = fit_results[0]
        fit_results[1] /= I0  # normalise to IO 
        residual, mits = fit_results[1:3]
        fit_results.append(maxits)
        self.pcov = pcov
        self.s_sq = s_sq

        if plot > 1:
            plt.scatter(self.v, self.i, c='r', s=80,
                        alpha=min(1, 4/np.sqrt(len(self.v))))

            self.plotchar(self.v, Te, Vf, I0, linewidth=4)
            # provide brief info about algorithm and Lnorm in the title
            plt.title('Te = {Te:.1f}eV, Vf = {Vf:.1f}, Isat = {I0:.3g}, <res> = {r:.2e} {mits} {A}{p}:its '
                      .format(Te=Te, Vf=Vf, I0=I0, r=residual, mits=mits, A=alg.upper()[0],p=Lnorm))
            plt.show(block=0)

        return(fit_results)