def Einzel(x, b, A): return ((A*lam)/(math.pi*np.sin(x/L)))**2 * (np.sin((math.pi*b*np.sin(x/L))/lam))**2 def Doppel(x, b, s): return 4*(np.cos((math.pi*s*np.sin(x/L))/lam))**2 * ((lam)/(math.pi*b*np.sin(x/L)))**2 * (np.sin((math.pi*b*np.sin(x/L))/lam))**2 print('-------------------------------------------------------------------') print('Einzelspalt b = 0.075mm') a1, i1 = np.loadtxt('data1.txt', unpack=True) i1 = i1 * 10**(-6) - Id a1 = a1 * 10**(-3) model = Model(Einzel, independent_vars=['x']) result = model.fit(i1, x=a1, b=Parameter(value=0.0001), A=Parameter(value=1)) print(result.params) plt.plot(a1, i1, 'gx', label='Messwerte') plt.plot(xwerte, Einzel(xwerte, **result.values), 'r-', label='gefittete Kurve') plt.ylabel('Intensität / A') plt.xlabel('a / m') plt.legend(loc="best") plt.grid() plt.tight_layout() plt.savefig('build/plot1.pdf') plt.close() print('-------------------------------------------------------------------') print('Einzelspalt b = 0.15mm')
def fit_jd_hist( hists: list, dt: float, D: list, fit_D: list, F: list, fit_F: list, sigma: float, fit_sigma: bool, verbose=False, ): """ Fits jd probability functions to a jd histograms. Parameters: hist (list): histogram values D (list): init values for MSD F (list): fractions for D, sum = 1 sigma (float): localization precision guess funcs (dict): dictionary with functions sigma, gamma, center, amplitude Returns: popt (lmfit.minimizerResult): optimized parameters """ from lmfit import Parameters, Parameter, minimize def residual(fit_params, data): res = cumulative_error_jd_hist(fit_params, data, len(D)) return res fit_params = Parameters() # fit_params.add('sigma', value=sigma, vary=fit_sigma, min=0.) fit_params.add("dt", value=dt, vary=False) try: fit_params.add("max_lag", value=max([h.lag for h in hists]), vary=False) except TypeError as e: logger.error( f"problem with `hists`: expected `list`,\ got `{type(hists)}`" ) raise e for i, (d, f_d, f, f_f) in enumerate(zip(D, fit_D, F, fit_F)): fit_params.add(f"D{i}", value=d, vary=f_d, min=0.0) fit_params.add(f"F{i}", value=f, min=0.0, max=1.0, vary=f_f) f_expr = "1" for i, f in enumerate(F[:-1]): f_expr += f" - F{i}" fit_params[f"F{i+1}"] = Parameter(name=f"F{i+1}", min=0.0, max=1.0, expr=f_expr) for i, (s, f_s, min_s, max_s) in enumerate( zip(sigma, fit_sigma, (0, sigma[0]), (3 * sigma[0], D[-1])) ): fit_params.add(f"sigma{i}", value=s, min=min_s, max=max_s, vary=f_s) logger.debug("start minimize") minimizer_result = minimize(residual, fit_params, args=(hists,)) if verbose: logger.info(f"completed in {minimizer_result.nfev} steps") minimizer_result.params.pretty_print() return minimizer_result
def parameters(self): if self._parameters is None: self._parameters = [Parameter(name) for name in self.module.parameters] return self._parameters
def addModel(self, event=None, model=None, is_step=False): if model is None and event is not None: model = event.GetString() if model is None or model.startswith('<'): return curmodels = ["c%i_" % (i+1) for i in range(1+len(self.fit_components))] for comp in self.fit_components: if comp in curmodels: curmodels.remove(comp) prefix = curmodels[0] label = "%s(prefix='%s')" % (model, prefix) title = "%s: %s" % (prefix[:-1], (model+' '*4)[:5]) mclass_kws = {'prefix': prefix} if is_step: form = model.lower() if form.startswith('err'): form = 'erf' label = "Step(form='%s', prefix='%s')" % (form, prefix) title = "%s: Step %s" % (prefix[:-1], form[:3]) mclass = lm_models.StepModel mclass_kws['form'] = form minst = mclass(form=form, prefix=prefix) else: mclass = getattr(lm_models, model+'Model') minst = mclass(prefix=prefix) panel = GridPanel(self.mod_nb, ncols=1, nrows=1, pad=1, itemstyle=CEN) def SLabel(label, size=(75, -1), **kws): return SimpleText(panel, label, size=size, style=wx.ALIGN_LEFT, **kws) usebox = Check(panel, default=True, label='Use?', size=(75, -1)) delbtn = Button(panel, 'Delete Model', size=(120, -1), action=partial(self.onDeleteComponent, prefix=prefix)) pick2msg = SimpleText(panel, " ", size=(75, -1)) pick2btn = Button(panel, 'Pick Data Range', size=(125, -1), action=partial(self.onPick2Points, prefix=prefix)) # SetTip(mname, 'Label for the model component') SetTip(usebox, 'Use this component in fit?') SetTip(delbtn, 'Delete this model component') SetTip(pick2btn, 'Select X range on Plot to Guess Initial Values') panel.Add(HLine(panel, size=(520, 3)), style=wx.ALIGN_CENTER, dcol=6) panel.Add(SLabel(label, size=(200, -1), colour='#0000AA'), dcol=3, newrow=True) panel.AddMany((usebox, pick2msg, pick2btn)) panel.Add(SLabel("Parameter"), newrow=True) panel.AddMany((SLabel("Value"), SLabel("Type"), SLabel("Min"), SLabel("Max"), SLabel("Expression"))) parwids = OrderedDict() parnames = sorted(minst.param_names) for a in minst._func_allargs: pname = "%s%s" % (prefix, a) if (pname not in parnames and a in minst.param_hints and a not in minst.independent_vars): parnames.append(pname) for pname in parnames: sname = pname[len(prefix):] hints = minst.param_hints.get(sname, {}) par = Parameter(name=pname, value=0, vary=True) if 'min' in hints: par.min = hints['min'] if 'max' in hints: par.max = hints['max'] if 'value' in hints: par.value = hints['value'] if 'expr' in hints: par.expr = hints['expr'] pwids = ParameterWidgets(panel, par, name_size=80, expr_size=150, float_size=70, prefix=prefix, widgets=('name', 'value', 'minval', 'maxval', 'vary', 'expr')) parwids[par.name] = pwids panel.Add(pwids.name, newrow=True) panel.AddMany((pwids.value, pwids.vary, pwids.minval, pwids.maxval, pwids.expr)) for sname, hint in minst.param_hints.items(): pname = "%s%s" % (prefix, sname) if 'expr' in hint and pname not in parnames: par = Parameter(name=pname, value=0, expr=hint['expr']) pwids = ParameterWidgets(panel, par, name_size=80, expr_size=275, float_size=70, prefix=prefix, widgets=('name', 'value', 'vary', 'expr')) parwids[par.name] = pwids panel.Add(pwids.name, newrow=True) panel.AddMany((pwids.value, pwids.vary)) panel.Add(pwids.expr, dcol=3, style=wx.ALIGN_RIGHT) pwids.value.Disable() pwids.vary.Disable() panel.Add(HLine(panel, size=(90, 3)), style=wx.ALIGN_CENTER, newrow=True) panel.Add(delbtn, dcol=2) panel.Add(HLine(panel, size=(250, 3)), dcol=3, style=wx.ALIGN_CENTER) fgroup = Group(prefix=prefix, title=title, mclass=mclass, mclass_kws=mclass_kws, usebox=usebox, panel=panel, parwids=parwids, float_size=65, expr_size=150, pick2_msg=pick2msg) self.fit_components[prefix] = fgroup panel.pack() self.mod_nb.AddPage(panel, title, True) sx,sy = self.GetSize() self.SetSize((sx, sy+1)) self.SetSize((sx, sy))
def fit_two_lz(x_lst, spectra_lst, first, last): ''' Give it a list x spectra and y spectra where to begin the fit and where to end it ''' # for gradient import numpy as np # for smoothing the curves import scipy.interpolate as interp #import splev # for non linear fitting from lmfit import minimize, Minimizer, Parameters, Parameter, report_fit # Restrict fitting region x_fit = [] y_fit = [] for x,y in zip(x_lst,spectra_lst): if first <= x and x <= last: x_fit.append(x) y_fit.append(y) # The length of the fitting region len_fit = len(x_fit) ''' F**K ME THIS IS BAD CODE ''' wiggle_room = .2 w_guess = 5 const = y_fit[0] ysmooth = interp.interp1d(x_fit, y_fit, kind='cubic') # normalize d/dx of smooth spectra # yp = list_normalizer(np.gradient(ysmooth(x_lst)).tolist(),1,0) yp = np.gradient(ysmooth(x_fit)).tolist() # normalize d2/dx2 of smooth spectra #ypp = list_normalizer(np.gradient(yp).tolist(),1,0) ypp = np.gradient(yp).tolist() # we want the peaks of -d2/dx2 ypp = [-x for x in ypp] ''' F**K ME THIS IS BAD CODE ''' # normalize spectra # don't normalize it # spectra_lst = list_normalizer(spectra_lst,1,0) # interpolate spectra to smooth ''' Start nailing down parameters ''' max_value = max(ypp) max_index = ypp.index(max_value) max_value = y_fit[max_index] peak1_guess = x_fit[max_index] ''' First peak is easy to find, now we have two regeims for the rest Are the peaks distinguishable, Yes or not ''' params = Parameters() ''' If one wanted to put in something very tricky, like say, fitting a Lorentzian to the LHS of the first peak and subtracting that LZ from the spectra, this is where you would do it ''' second_index = len_fit - 5 try: # Find maximum in ypp on the range restricted to what's left of the spectra second_value = 0 for x,y in zip(x_fit,ypp): if x > x_fit[max_index]: if y > second_value: second_value = y second_index = x_fit.index(x) except ValueError: second_value = y_fit[max_index - 4] print "Value Error in finding second index" # Nail down the index and value for second peak peak2_guess = x_fit[second_index] second_value = y_fit[second_index] # This checks that the centers are *actually* distinguishable if not (9 < abs(peak1_guess - peak2_guess) < 11): peak2_guess = peak1_guess + 10 ''' Now we make the parameters dictionary for lmfit ''' params.add('center1',value=peak1_guess,min=peak1_guess-wiggle_room*len_fit,max=peak1_guess+wiggle_room*len_fit, vary=True) params.add('center2',value=peak2_guess,min=peak2_guess-wiggle_room*len_fit,max=peak2_guess+wiggle_room*len_fit, vary=True) params.add('width1', value = w_guess, min = 0, max =5*w_guess, vary=True) params.add('width2', value = w_guess, min = 0, max =5*w_guess, vary=True) params.add('const1', value = const, min = -.1, max = .1, vary=True) params.add('const2', value = const, expr='const1',min = -.1, max = .1, vary=False) # scaling is given by # Height max = scaling*2/(Pi* width) s1 = max_value*(2.0/3.14159*(params['width1'])**-1)**-1 s2 = second_value*(2.0/3.14159*(params['width2'])**-1)**-1 # Functionally the same as what's above params['scaling1'] = Parameter(name='scaling1', value = s1,min = 0, max = 2*s1, vary=True) params['scaling2'] = Parameter(name='scaling2', value = s2,min = 0, max = 2*s2, vary=True) ''' We have the parameters! Time to do actual fitting ''' # Work horse minner = Minimizer(DoubleLorentzian, params, fcn_args=(x_fit, y_fit)) result = minner.minimize(method='least_squares') return_dict = {} for x in sorted(result.params): return_dict[x] = result.params[x].value return return_dict
def Ecal_old(detectors, motor, guessed_energy, mode, *, guessed_amplitude=0.5, guessed_sigma=0.004, min_step=0.001, D='LaB6', max_n=3, margin=0.5, md=None): """ Energy calibration scan Parameters ---------- detectors : detectors motor : the motor guessed_energy : number units of keV mode : {'peaks', 'dips'} guessed_amplitude : number, optional detector units, defaults to 1500 guessed_sigma : number, optional in units of degrees, defaults to 0.004 min_step : number, optional step size in degrees, defaults to 0.001 D : string or array, optional Either provide the spacings literally (as an array) or give the name of a known spacing ('LaB6' or 'Si') max_n : integer, optional how many peaks (on one side of 0), default is 3 margin : number, optional how far to scan in two theta beyond the guessed left and right peaks, default 0.5 Example ------- Execute an energy calibration scan with default steps. >>> RE(Ecal(68)) """ if mode == 'peaks': #motor = tth_cal factor = 2 offset = 0 sign = 1 if mode == 'dips': #motor = th_cal factor = 1 # theta_hardware = theta_theorhetical + offset offset = -35.26 # degrees sign = -1 if isinstance(D, str): D = D_SPACINGS[D] # Based on the guessed energy, compute where the peaks should be centered # in theta. This will be used as an initial guess for peak-fitting. guessed_wavelength = 12.398 / guessed_energy # angtroms theta = np.rad2deg(np.arcsin(guessed_wavelength / (2 * D))) guessed_centers = factor * theta # 'factor' puts us in two-theta units if applicable _range = max(guessed_centers) + (factor * margin) # Scan from positive to negative because that is the direction # that the th_cal and tth_cal motors move without backlash. start, stop = _range + offset, -_range + offset print('guessed_wavelength={} [Angstroms]'.format(guessed_wavelength)) print('guessed_centers={} [in {}-theta DEGREES]'.format( guessed_centers, factor)) print('will scan from {} to {} in hardware units'.format(start, stop)) if max_n > 3: raise NotImplementedError("I only work for n up to 3.") # todo : make a model and sum them def peaks(x, c0, wavelength, a1, a2, sigma): # x comes from hardware in [theta or two-theta] degrees x = np.deg2rad(x / factor - offset) # radians assert np.all(wavelength < 2 * D), \ "wavelength would result in illegal arg to arcsin" cs = np.arcsin(wavelength / (2 * D)) c1 = cs[0] c2 = cs[1] #c3 = cs[2] # first peak result = ( voigt(x=x, amplitude=sign * a1, center=c0 - c1, sigma=sigma) + voigt(x=x, amplitude=sign * a1, center=c0 + c1, sigma=sigma)) # second peak result += ( voigt(x=x, amplitude=sign * a2, center=c0 - c2, sigma=sigma) + voigt(x=x, amplitude=sign * a2, center=c0 + c2, sigma=sigma)) # third peak #result += (voigt(x=x, amplitude=sign*a3, center=c0 - c3, sigma=sigma) + #voigt(x=x, amplitude=sign*a3, center=c0 + c3, sigma=sigma)) return result model = Model(peaks) + LinearModel() # Fill out initial guess. init_guess = { 'intercept': Parameter('intercept', value=0, min=-100, max=100), 'slope': Parameter('slope', value=0, min=-100, max=100), 'sigma': Parameter('sigma', value=np.deg2rad(guessed_sigma)), 'c0': Parameter('c0', value=np.deg2rad(0), min=-0.2, max=0.2), 'wavelength': Parameter('wavelength', guessed_wavelength, min=0.8 * guessed_wavelength, max=1.2 * guessed_wavelength) } # min=0, max=np.min(2 * D))} kwargs = {'min': 0.5 * guessed_amplitude, 'max': 2 * guessed_amplitude} for i, center in enumerate(guessed_centers): init_guess.update({ 'a%d' % (1 + i): Parameter('a%d' % (1 + i), guessed_amplitude, **kwargs) }) print(init_guess) lf = LiveFit(model, detectors[0].name, {'x': motor.name}, init_guess, update_every=100) fig, ax = plt.subplots() # explitly create figure, axes to use below plot = LivePlot(detectors[0].name, motor.name, linestyle='none', marker='o', ax=ax) lfp = LiveFitPlot(lf, ax=ax, color='r') subs = [lfp, plot] #detectors = [det]#[sc] # Set up metadata -- based on the sourcecode of bluesky.plans.scan. _md = { 'detectors': [det.name for det in detectors], 'motors': [motor.name], 'hints': {}, } _md.update(md or {}) try: dimensions = [(motor.hints['fields'], 'primary')] except (AttributeError, KeyError): pass else: _md['hints'].setdefault('dimensions', dimensions) initial_steps = np.arange(start, stop, -min_step) assert len(initial_steps), "bad start, stop, min_step parameters" size = factor * 0.05 # region around each predicted peak location @bpp.stage_decorator(list(detectors) + [motor]) @bpp.run_decorator(md=_md) def inner_scan(): wavelength = guessed_wavelength for step in initial_steps: yield from bps.one_1d_step(detectors, motor, step) x_data = lf.independent_vars_data['x'] if x_data and lf.result is not None: # Have we yet scanned past the third peak? wavelength = lf.result.values['wavelength'] # Convert c's to hardware units here for comparison with x_data. c1, c2, c3 = factor * np.rad2deg( np.arcsin(wavelength / (2 * D))) + offset if np.min(x_data) < (c1 - 1): # Stop dense scanning. print('Preliminary result:\n', lf.result.values) print('Becoming adaptive to save time....') break # left of zero peaks c1, c2, c3 = -factor * np.rad2deg(np.arcsin(wavelength / (2 * D))) + offset neighborhoods = [ np.arange(c + size, c - size, -min_step) for c in (c1, c2, c3) ] for neighborhood in neighborhoods: for step in neighborhood: yield from bps.one_1d_step(detectors, motor, step) plan = inner_scan() plan = bpp.subs_wrapper(plan, subs) plan = bpp.reset_positions_wrapper(plan, [motor]) ret = (yield from plan) print(lf.result.values) print('WAVELENGTH: {} [Angstroms]'.format(lf.result.values['wavelength'])) return ret
def test_constraints2(): """add a user-defined function to symbol table""" def residual(pars, x, sigma=None, data=None): yg = gaussian(x, pars['amp_g'].value, pars['cen_g'].value, pars['wid_g'].value) yl = lorentzian(x, pars['amp_l'].value, pars['cen_l'].value, pars['wid_l'].value) slope = pars['line_slope'].value offset = pars['line_off'].value model = yg + yl + offset + x * slope if data is None: return model if sigma is None: return (model - data) return (model - data) / sigma n = 601 xmin = 0. xmax = 20.0 x = linspace(xmin, xmax, n) data = (gaussian(x, 21, 8.1, 1.2) + lorentzian(x, 10, 9.6, 2.4) + random.normal(scale=0.23, size=n) + x * 0.5) pfit = [ Parameter(name='amp_g', value=10), Parameter(name='cen_g', value=9), Parameter(name='wid_g', value=1), Parameter(name='amp_tot', value=20), Parameter(name='amp_l', expr='amp_tot - amp_g'), Parameter(name='cen_l', expr='1.5+cen_g'), Parameter(name='line_slope', value=0.0), Parameter(name='line_off', value=0.0) ] sigma = 0.021 # estimate of data error (for all data points) myfit = Minimizer(residual, pfit, fcn_args=(x, ), fcn_kws={ 'sigma': sigma, 'data': data }, scale_covar=True) def width_func(wpar): """ """ return 2 * wpar myfit.params._asteval.symtable['wfun'] = width_func myfit.params.add(name='wid_l', expr='wfun(wid_g)') myfit.leastsq() print(' Nfev = ', myfit.nfev) print(myfit.chisqr, myfit.redchi, myfit.nfree) report_fit(myfit.params) pfit = myfit.params fit = residual(myfit.params, x) assert (pfit['cen_l'].value == 1.5 + pfit['cen_g'].value) assert (pfit['amp_l'].value == pfit['amp_tot'].value - pfit['amp_g'].value) assert (pfit['wid_l'].value == 2 * pfit['wid_g'].value)
############################################################################### # We can review the best-fit `Parameters` by accessing `result.params`: result.params.pretty_print() ############################################################################### # More information about the fit is stored in the result, which is an # ``lmfit.MimimizerResult`` object (see: # https://lmfit.github.io/lmfit-py/fitting.html#lmfit.minimizer.MinimizerResult) ############################################################################### # **Specifying Bounds and Holding Parameters Constant** # # Above, the ``Model`` class implicitly builds ``Parameter`` objects from # keyword arguments of ``fit`` that match the argments of ``decay``. You can # build the ``Parameter`` objects explicity; the following is equivalent. result = model.fit(data, t=t, N=Parameter(value=10), tau=Parameter(value=1)) report_fit(result.params) ############################################################################### # By building ``Parameter`` objects explicitly, you can specify bounds # (``min``, ``max``) and set parameters constant (``vary=False``). result = model.fit(data, t=t, N=Parameter(value=7, vary=False), tau=Parameter(value=1, min=0)) report_fit(result.params) ############################################################################### # **Defining Parameters in Advance** # # Passing parameters to ``fit`` can become unwieldly. As an alternative, you
Af[i] = math.sqrt(As[i]) / 10 i += 1 print(Af) print('---------------------------------------------------------------------') #Charakteristik Ux = U[3:12] Ax = A[3:12] def Fit(x, m, b): return x * m + b model = Model(Fit, independent_vars=['x']) result = model.fit(Ax / 10, x=Ux, b=Parameter(value=1), m=Parameter(value=0.1)) print('Steigung =', 0.090864197530844279, '+-', 0.0222) print('y-Achse =', 503.69013096345441, '+-', 11.4) Stei = (ufloat(0.090864197530844279, 0.0222) * 100) / 545 print('Steigung in Prozent =', Stei * 100) print('---------------------------------------------------------------------') xWert = np.linspace(300, 750, 1000) plt.errorbar(Ux, Ax / 10, yerr=Af, fmt='x', ecolor='b', label='relevante Messwerte') plt.plot(U[0:2], A[0:2] / 10, 'gx') plt.plot(U[13:16], A[13:16] / 10, 'gx')
print('-------------------------------------------------------------------') print('Exponenten des Raumladungsgesetzes') a = 0.0002176 konst = 4 / 9 * c.epsilon_0 * (2 * c.e / c.m_e)**(0.5) * f1 / a**2 print('m = ', konst) print('--------------------') def Raumladungsgesetz(V, x): return konst * (V**x) model = Model(Raumladungsgesetz, independent_vars=['V']) result1 = model.fit(I1, V=U_1, x=Parameter(value=1.5)) print('I1 = ', result1.params) print('--------------------') result2 = model.fit(I2, V=U_1, x=Parameter(value=1.5)) print('I2 = ', result2.params) print('--------------------') plt.plot(U_1, I1, 'rx', label='$I_1 = 2.5$ A') plt.plot(U_1, I2, 'gx', label='$I_2 = 2.4$ A') plt.plot(U_1, Raumladungsgesetz(U_1, 1.378684), 'r-', label='$I_1 = 2.5$ A') plt.plot(U_1, Raumladungsgesetz(U_1, 1.344863), 'g-', label='$I_2 = 2.4$ A') plt.ylabel('I / mA') plt.xlabel('U / V') plt.legend(loc="best") plt.grid()
def run_bragg_peak(self, update_table_ui=False, list_row_to_fit=None): gmodel = Model(kropff_bragg_peak_tof, nan_policy='propagate', independent_vars=['lda']) lda = self.xaxis_to_fit o_gui = GuiUtility(parent=self.parent) ldahkl_init = np.float( str(self.parent.ui.kropff_bragg_peak_ldahkl_init.text())) tau_init = np.float(str(self.parent.kropff_bragg_peak_tau_init.text())) sigma_init = np.float( self.parent.kropff_bragg_peak_sigma_comboBox.currentText()) for _row, yaxis in enumerate(self.list_yaxis_to_fit): if not list_row_to_fit is None: if _row not in list_row_to_fit: continue _entry_high = self.parent.fitting_input_dictionary['rois'][_row][ 'fitting']['kropff']['high'] a0 = np.float(_entry_high['a0']) b0 = np.float(_entry_high['b0']) _entry_low = self.parent.fitting_input_dictionary['rois'][_row][ 'fitting']['kropff']['low'] ahkl = np.float(_entry_low['ahkl']) bhkl = np.float(_entry_low['bhkl']) yaxis = -np.log(yaxis) _result = gmodel.fit(yaxis, lda=lda, a0=Parameter('a0', value=a0, vary=False), b0=Parameter('b0', value=b0, vary=False), ahkl=Parameter('ahkl', value=ahkl, vary=False), bhkl=Parameter('bhkl', value=bhkl, vary=False), ldahkl=ldahkl_init, sigma=sigma_init, tau=tau_init) ldahkl = _result.params['ldahkl'].value ldahkl_error = _result.params['ldahkl'].stderr sigma = _result.params['sigma'].value sigma_error = _result.params['sigma'].stderr tau = _result.params['tau'].value tau_error = _result.params['tau'].stderr yaxis_fitted = kropff_bragg_peak_tof(self.xaxis_to_fit, a0, b0, ahkl, bhkl, ldahkl, sigma, tau) result_dict = { 'ldahkl': ldahkl, 'ldahkl_error': ldahkl_error, 'sigma': sigma, 'sigma_error': sigma_error, 'tau': tau, 'tau_error': tau_error, 'xaxis_to_fit': lda, 'yaxis_fitted': yaxis_fitted } self.parent.fitting_input_dictionary['rois'][_row]['fitting'][ 'kropff']['bragg_peak'] = deepcopy(result_dict) if update_table_ui: o_gui.update_kropff_bragg_edge_table_ui( row=_row, ldahkl=ldahkl, ldahkl_error=ldahkl_error, tau=tau, tau_error=tau_error, sigma=sigma, sigma_error=sigma_error)
def test_sum_of_two_gaussians(self): # two user-defined gaussians model1 = self.model f2 = lambda x, height_, center_, sigma_: gaussian( x, height_, center_, sigma_) model2 = Model(f2, ['x']) values1 = self.true_values() values2 = self.true_values() values2['sigma'] = 1.5 values2['height'] = 4 data = gaussian(x=self.x, **values1) data += gaussian(x=self.x, **values2) model = self.model + model2 values2 = {k + '_': v for k, v in values2.items()} guess = { 'sigma': Parameter(value=2, min=0), 'center': 1, 'height': 1, 'sigma_': Parameter(value=1, min=0), 'center_': 1, 'height_': 1 } true_values = dict(list(values1.items()) + list(values2.items())) result = model.fit(data, x=self.x, **guess) assert_results_close(result.values, true_values) # user-defined models with common parameter names # cannot be added, and should raise f = lambda: model1 + model1 self.assertRaises(NameError, f) # two predefined_gaussians, using suffix to differentiate model1 = specified_models.Gaussian(['x']) model2 = specified_models.Gaussian(['x'], suffix='_') model = model1 + model2 true_values = { 'center': values1['center'], 'height': values1['height'], 'sigma': values1['sigma'], 'center_': values2['center_'], 'height_': values2['height_'], 'sigma_': values2['sigma_'] } guess = { 'sigma': 2, 'center': 1, 'height': 1, 'sigma_': 1, 'center_': 1, 'height_': 1 } result = model.fit(data, x=self.x, **guess) assert_results_close(result.values, true_values) # without suffix, the names collide and Model should raise model1 = specified_models.Gaussian(['x']) model2 = specified_models.Gaussian(['x']) f = lambda: model1 + model2 self.assertRaises(NameError, f)
#Auslenkung in Grad für Rubidium R_r = (np.array([343.7, 344.5]) - HM) * (math.pi/180) tR_r = 2.92 print('------------------------------------------------------------------') print('Nr. a: Berechnung der Gitterkonstanten g') Rad_H = np.sin((np.array([331.1, 331.7, 333.2, 334.6, 335.4, 335.6, 341.4, 347.3, 350.5]) - HM) * (math.pi/180)) Lambda = np.array([438.8, 447.1, 471.3, 492.2, 501.6, 504.8, 587.6, 667.8, 706.5]) * 10**(-9) def Gitter(x, m, b): return x/m + b model = Model(Gitter, independent_vars=['x']) result = model.fit(Rad_H, x=Lambda, b=Parameter(value=0.1), m=Parameter(value=10**(-6))) print(result.params) x_Wert = np.linspace(400, 750, 10000) * 10**(-9) plt.plot(Lambda * 10**(9), Rad_H, 'rx', label='Messwerte') plt.plot(x_Wert * 10**(9), Gitter(x_Wert, **result.params), 'b-', label='Ausgleichsgerade') plt.xlabel('$\lambda$ / $10^{-9}$ m') plt.ylabel('$\sin(\phi)$') plt.legend(loc="best") plt.grid() plt.tight_layout() plt.savefig('build/Gitterkonstante.pdf') plt.close() print('------------------------------------------------------------------') print('Nr. b: Berechnung der Eichgröße')
def fit_decaying_oscillation(y_array, x_array, d=2, freq_init=None, amp_init=None, phase_init=0, y0_init=None): """ Fit y_array with Gaussian decay oscillations. """ def oscillation(t, y0, amplitude, freq, t2, phase): return y0 + amplitude * np.exp(-(t/t2)**d) * np.cos(2*pi*freq*t + phase) time_scale = x_array[-1] - x_array[0] if freq_init is None: freq_init = 10.0/time_scale if amp_init is None: amp_init = y_array[0] - y_array[-1] if y0_init is None: y0_init = y_array[-1] model = lmfit.Model(oscillation, independent_vars=['t']) result = model.fit(y_array, t=x_array, y0=y0_init, amplitude=amp_init, freq=freq_init, t2=Parameter(value=time_scale/2, min=0), phase=phase_init) return result
p_true.add('cen_g', value=8.1) p_true.add('wid_g', value=1.6) p_true.add('frac', value=0.37) p_true.add('line_off', value=-1.023) p_true.add('line_slope', value=0.62) data = (pvoigt(x, p_true['amp_g'].value, p_true['cen_g'].value, p_true['wid_g'].value, p_true['frac'].value) + random.normal(scale=0.23, size=n) + x * p_true['line_slope'].value + p_true['line_off'].value) if HASPYLAB: pylab.plot(x, data, 'r+') pfit = [ Parameter(name='amp_g', value=10), Parameter(name='amp_g', value=10.0), Parameter(name='cen_g', value=9), Parameter(name='wid_g', value=1), Parameter(name='frac', value=0.50), Parameter(name='amp_l', expr='amp_g'), Parameter(name='cen_l', expr='cen_g'), Parameter(name='wid_l', expr='wid_g'), Parameter(name='line_slope', value=0.0), Parameter(name='line_off', value=0.0) ] sigma = 0.021 # estimate of data error (for all data points) myfit = Minimizer(residual, pfit,
def __init__(self, config): defaultRkpVary = True defaultRkpMin = float("-inf") defaultRkpMax = float("inf") params = Parameters() equationName = "" try: equationName = config['equation'] if equationName == "density": params['c1'] = Parameter('c1', value=(1.0 / config['compounds'][0]['density']), vary=False) params['c2'] = Parameter('c2', value=(1.0 / config['compounds'][1]['density']), vary=False) params['c3'] = Parameter('c3', value=(1.0 / config['compounds'][2]['density']), vary=False) elif equationName == "volume": params['c1'] = Parameter('c1', value=config['compounds'][0]['volume'], vary=False) params['c2'] = Parameter('c2', value=config['compounds'][1]['volume'], vary=False) params['c3'] = Parameter('c3', value=config['compounds'][2]['volume'], vary=False) elif equationName == "conductivity": params['c1'] = Parameter('c1', value=config['compounds'][0]['conductivity'], vary=False) params['c2'] = Parameter('c2', value=config['compounds'][1]['conductivity'], vary=False) params['c3'] = Parameter('c3', value=config['compounds'][2]['conductivity'], vary=False) else: raise ValueError("Unknown equation '{}'".format(config['equation'])) except KeyError: equationName = "density" print "Equation parameter must be set. Using 'density' equation ..." params['c1'] = Parameter('c1', value=(1.0 / config['compounds'][0]['density']), vary=False) params['c2'] = Parameter('c2', value=(1.0 / config['compounds'][1]['density']), vary=False) params['c3'] = Parameter('c3', value=(1.0 / config['compounds'][2]['density']), vary=False) rkp12Conf = config['binary']['rkp']['x1-x2'] params['rkp12_1'] = \ Parameter('rkp12_1', \ value=rkp12Conf[0]['value'], vary=(rkp12Conf[0]['vary'] if 'vary' in rkp12Conf[0] else defaultRkpVary), \ min=(rkp12Conf[0]['min'] if 'min' in rkp12Conf[0] else defaultRkpMin), \ max=(rkp12Conf[0]['max'] if 'max' in rkp12Conf[0] else defaultRkpMax)) params['rkp12_2'] = \ Parameter('rkp12_2', \ value=rkp12Conf[1]['value'], vary=(rkp12Conf[1]['vary'] if 'vary' in rkp12Conf[1] else defaultRkpVary), \ min=(rkp12Conf[1]['min'] if 'min' in rkp12Conf[1] else defaultRkpMin), \ max=(rkp12Conf[1]['max'] if 'max' in rkp12Conf[1] else defaultRkpMax)) params['rkp12_3'] = \ Parameter('rkp12_3', \ value=rkp12Conf[2]['value'], vary=(rkp12Conf[2]['vary'] if 'vary' in rkp12Conf[2] else defaultRkpVary), \ min=(rkp12Conf[2]['min'] if 'min' in rkp12Conf[2] else defaultRkpMin), \ max=(rkp12Conf[2]['max'] if 'max' in rkp12Conf[2] else defaultRkpMax)) rkp13Conf = config['binary']['rkp']['x1-x3'] params['rkp13_1'] = \ Parameter('rkp13_1', \ value=rkp13Conf[0]['value'], vary=(rkp13Conf[0]['vary'] if 'vary' in rkp13Conf[0] else defaultRkpVary), \ min=(rkp13Conf[0]['min'] if 'min' in rkp13Conf[0] else defaultRkpMin), \ max=(rkp13Conf[0]['max'] if 'max' in rkp13Conf[0] else defaultRkpMax)) params['rkp13_2'] = \ Parameter('rkp13_2', \ value=rkp13Conf[1]['value'], vary=(rkp13Conf[1]['vary'] if 'vary' in rkp13Conf[1] else defaultRkpVary), \ min=(rkp13Conf[1]['min'] if 'min' in rkp13Conf[1] else defaultRkpMin), \ max=(rkp13Conf[1]['max'] if 'max' in rkp13Conf[1] else defaultRkpMax)) params['rkp13_3'] = \ Parameter('rkp13_3', \ value=rkp13Conf[2]['value'], vary=(rkp13Conf[2]['vary'] if 'vary' in rkp13Conf[2] else defaultRkpVary), \ min=(rkp13Conf[2]['min'] if 'min' in rkp13Conf[2] else defaultRkpMin), \ max=(rkp13Conf[2]['max'] if 'max' in rkp13Conf[2] else defaultRkpMax)) rkp23Conf = config['binary']['rkp']['x2-x3'] params['rkp23_1'] = \ Parameter('rkp23_1', \ value=rkp23Conf[0]['value'], vary=(rkp23Conf[0]['vary'] if 'vary' in rkp23Conf[0] else defaultRkpVary), \ min=(rkp23Conf[0]['min'] if 'min' in rkp23Conf[0] else defaultRkpMin), \ max=(rkp23Conf[0]['max'] if 'max' in rkp23Conf[0] else defaultRkpMax)) params['rkp23_2'] = \ Parameter('rkp23_2', \ value=rkp23Conf[1]['value'], vary=(rkp23Conf[1]['vary'] if 'vary' in rkp23Conf[1] else defaultRkpVary), \ min=(rkp23Conf[1]['min'] if 'min' in rkp23Conf[1] else defaultRkpMin), \ max=(rkp23Conf[1]['max'] if 'max' in rkp23Conf[1] else defaultRkpMax)) params['rkp23_3'] = \ Parameter('rkp23_3', \ value=rkp23Conf[2]['value'], vary=(rkp23Conf[2]['vary'] if 'vary' in rkp23Conf[2] else defaultRkpVary), \ min=(rkp23Conf[2]['min'] if 'min' in rkp23Conf[2] else defaultRkpMin), \ max=(rkp23Conf[2]['max'] if 'max' in rkp23Conf[2] else defaultRkpMax)) rkp123Conf = config['ternary']['rkp'] params['rkp123_1'] = \ Parameter('rkp123_1', \ value=rkp123Conf[0]['value'], vary=(rkp123Conf[0]['vary'] if 'vary' in rkp123Conf[0] else defaultRkpVary), \ min=(rkp123Conf[0]['min'] if 'min' in rkp123Conf[0] else defaultRkpMin), \ max=(rkp123Conf[0]['max'] if 'max' in rkp123Conf[0] else defaultRkpMax)) params['rkp123_2'] = \ Parameter('rkp123_2', \ value=rkp123Conf[1]['value'], vary=(rkp123Conf[1]['vary'] if 'vary' in rkp123Conf[1] else defaultRkpVary), \ min=(rkp123Conf[1]['min'] if 'min' in rkp123Conf[1] else defaultRkpMin), \ max=(rkp123Conf[1]['max'] if 'max' in rkp123Conf[1] else defaultRkpMax)) params['rkp123_3'] = \ Parameter('rkp123_3', \ value=rkp123Conf[2]['value'], vary=(rkp123Conf[2]['vary'] if 'vary' in rkp123Conf[2] else defaultRkpVary), \ min=(rkp123Conf[2]['min'] if 'min' in rkp123Conf[2] else defaultRkpMin), \ max=(rkp123Conf[2]['max'] if 'max' in rkp123Conf[2] else defaultRkpMax)) # data rawdata = np.array(config['data']) rawdataLength = len(rawdata) data = np.delete(rawdata, 4, 1) exp = np.delete(rawdata, [0, 1, 2, 3], 1).reshape((rawdataLength,)) if equationName == "density": for i in range(len(exp)): exp[i] = (1.0 / exp[i]) if exp[i] != 0.0 else 0.0 # set properties self.equationModel = EquationModel() self.params = params self.data = data self.exp = exp
def fit_Ecal_dips_symmetric(xdata, ydata, guessed_sigma=.01, wguess=66., D="Si", theta_offset=-35.26): ''' This fits for two symmetric peaks. Parameters ---------- xdata : the xdata (usually theta) ydata : the counts measured guessed_sigma: float, optional the guessed sigma of the peaks (supplied to initial guess) wguess : the guessed wavelength D : str The str identifier for the reference sample (usually just "Si") theta_offset : The offset in theta of the theta motor ''' # this is a cludge used to pass result around. # TODO : remove this when we finally agree on a method on how to scan... if isinstance(D, str): D = D_SPACINGS[D] guessed_wavelength = wguess # theta-tth factor factor = 1 offset = theta_offset # degrees # TODO : Make a Model #def dips(x, c0, wavelength, a1, a2, sigma): def dips(x, c0, wavelength, a1, sigma): sign = -1 # x comes from hardware in [theta or two-theta] degrees x = np.deg2rad(x / factor - offset) # radians assert np.all(wavelength < 2 * D), \ "wavelength would result in illegal arg to arcsin" centers = np.arcsin(wavelength / (2 * D)) # just look at one center for now c1 = centers[0] result = ( voigt(x=x, amplitude=sign * a1, center=c0 - c1, sigma=sigma) + voigt(x=x, amplitude=sign * a1, center=c0 + c1, sigma=sigma)) return result model = Model(dips) + LinearModel() guessed_average = np.max(ydata) guessed_amplitude = np.abs(np.min(ydata) - np.mean(ydata)) # Fill out initial guess. init_guess = { 'intercept': Parameter( 'intercept', value=guessed_average, ), 'slope': Parameter('slope', value=0, min=-100, max=100, vary=False), 'sigma': Parameter('sigma', value=np.deg2rad(guessed_sigma)), 'c0': Parameter('c0', value=np.deg2rad(0)), 'wavelength': Parameter('wavelength', guessed_wavelength, min=0.8 * guessed_wavelength, max=1.2 * guessed_wavelength), 'a1': Parameter('a1', guessed_amplitude, min=0), } params = Parameters(init_guess) # fit_kwargs = dict(ftol=1) fit_kwargs = dict() result = model.fit(ydata, x=xdata, params=params, fit_kwargs=fit_kwargs) print('WAVELENGTH: {} [Angstroms]'.format(result.values['wavelength'])) print('CENTER is : {} [deg]'.format(result.values['c0'])) plt.figure(2) plt.clf() plt.plot(xdata, ydata, linewidth=0, marker='o', color='b', label="data") plt.plot(xdata, result.best_fit, color='r', label="best fit") plt.legend() return result
def pool_data(files, dt, minframe=5, maxframe=10000, rsquared_threshold=0.0, fit_option=2, display_id=False, dataframe=True): """This function reads the trajectories in all of the files, computes the T-MSD, performs a fit to evaluate D and alpha. Parameters ---------- files : list of str The list of the TrackMate CSV filenames, containing columns POSITION_X, POSITION_Y and TRACK_ID dt : float time step between each position in the trajectories minframe, maxframe : int filter on the minimum / maximum number of frames per retained trajectory. Default set to respectively 5 and 10000. rsquared_threshold : float <= 1 filter on the minimum value for the coefficient of determination during the fit of the T-MSDs. Default is 0.0. fit_option : int or list or str options for the points to consider during the fit of the T-MSD. If int, the fit is performed over the N first points. If list the fit is performed from the first element to the second. str option available: "thirty_percent". Default is 2. display_id : bool, optional prints the ID of the tracks as they are being processed. Useful if the analysis is too slow. Default is disabled. dataframe : bool, optional returns a DataFrame instead of a numpy array. The diffusion coefficient is directly transformed to its log10 form. Default is enabled. Returns ------- df a DataFrame collecting all of the generated data DATA a numpy matrix containing the same information """ #Initial tests: if isinstance(files, list) == False: print( "Please provide a list of TrackMate CSV files, with columns POSITION_X, POSITION_Y and TRACK_ID." ) return else: print("Parameters for the MSD analysis: dt = ", dt) print('Initial filters: minframe = ', minframe, ', maxframe = ', maxframe, ', R2 threshold = ', rsquared_threshold) print("Fit option: ", fit_option) #Parameters for the fit minalpha = 0.1 minD = 1.0E-06 maxD = 1 maxalpha = 3 def msdlog(logt, D, alpha): return (alpha * logt + np.log(4 * D)) msdlog_model = Model(msdlog) params = Parameters() params['alpha'] = Parameter(name='alpha', value=1.0, min=minalpha, max=maxalpha) params['D'] = Parameter(name='D', value=0.1, min=minD, max=maxD) #Interpret fit option that does not depend on the tracklength if isinstance(fit_option, int): nbrpts = fit_option n0 = 0 elif isinstance(fit_option, list): nbrpts = fit_option[1] n0 = fit_option[0] DATA = [] #Initialize the DATA output matrix print("Reading filenames in ", str(files), '...') for p in range(len(files)): filename = files[p] print('Analysis for', filename, '...') data = pd.read_csv(filename) tracklist = data.TRACK_ID.unique() #list of track IDs in the file conserved_tracks = 0 idxt = 0 for tid in tracklist: if display_id == True: print('Track ' + str(idxt) + ' out of ' + str(len(tracklist))) trackid = data["TRACK_ID"] == tid x = data[trackid]["POSITION_X"].to_numpy( ) #x associated to track 'tid' y = data[trackid]["POSITION_Y"].to_numpy( ) #y associated to track 'tid' N = len(x) if N <= maxframe and N >= minframe: tmsd, timelag = T_MSD(x, y, dt) #Interpret fit option if fit_option == "thirty_percent": nbrpts = round(0.31 * N) n0 = 0 result = msdlog_model.fit( [np.log(k) for k in tmsd[n0:nbrpts]], params, logt=[np.log(k) for k in timelag[n0:nbrpts]]) alpha = result.params['alpha'].value D = result.params['D'].value N_for_R2 = round( 0.6 * len(tmsd)) #evaluate R2 on a third of the MSD length y_true = tmsd[:N_for_R2] y_pred = [4 * D * ndt**alpha for ndt in timelag[:N_for_R2]] rsquare = r2_score(y_true, y_pred) c = confinement_ratio(x, y) if rsquare > rsquared_threshold: feat = [alpha, D, c, rsquare, N, x, y, tmsd, filename] DATA.append(feat) conserved_tracks += 1 idxt += 1 print(conserved_tracks, " tracks were kept out of ", len(trackid), '. Done.') if dataframe == True: print("Generating a DataFrame...") df = pd.DataFrame(DATA, columns=[ 'alpha', 'D', 'c', 'R2', 'N', 'x', 'y', 'MSD', 'Filename' ]) df['D'] = df['D'].map(lambda x: np.log10(x)) print("End of the program. Returning DataFrame.") return (df) else: print("End of the program. Returning numpy array.") return (DATA)
def calib_g(df_fc_suews, g_max=33.1, lai_max=5.9, s1=5.56, method='cobyla', prms_init=None, debug=False): """Calibrate parameters for modelling surface conductance over vegetated surfaces using `LMFIT <https://lmfit.github.io/lmfit-py/model.html>`. Parameters ---------- df_fc_suews : pandas.DataFrame DataFrame in `SuPy forcing <https://supy.readthedocs.io/en/latest/data-structure/df_forcing.html>`_ format g_max : numeric, optional Maximum surface conductance [mm s-1], by default 30 lai_max : numeric, optional Maximum LAI [m2 m-2], by default 6 s1 : numeric, optional Wilting point (WP=s1/g6, indicated as deficit [mm]) related parameter, by default 5.56 method: str, optional Method used in minimisation by `lmfit.minimize`: details refer to its `method<lmfit:minimize>`. prms_init: lmfit.Parameters Initial parameters for calibration debug : bool, optional Option to output final calibrated `ModelResult <lmfit:ModelResult>`, by default False Returns ------- dict, or `ModelResult <lmfit:ModelResult>` if `debug==True` 1. dict: {parameter_name -> best_fit_value} 2. `ModelResult` Note: Parameters for surface conductance: g1 (LAI related), g2 (solar radiation related), g3 (humidity related), g4 (humidity related), g5 (air temperature related), g6 (soil moisture related) Note ---- For calibration validity, turbulent fluxes, QH and QE, in `df_fc_suews` should ONLY be observations, i.e., interpolated values should be avoided. To do so, please place `np.nan` as missing values for QH and QE. """ list_var_sel = ['qh', 'qe', 'Tair', 'RH', 'pres', 'kdown', 'xsmd', 'lai'] df_obs = df_fc_suews[list_var_sel].copy().dropna() df_obs.pres *= 100 df_obs.Tair += 273.15 gs_obs = cal_gs_obs(df_obs.qh, df_obs.qe, df_obs.Tair, df_obs.RH, df_obs.pres) def func_fit_g(kd, ta, rh, pa, smd, lai, g1, g2, g3, g4, g5, g6): return cal_gs_mod(kd, ta, rh, pa, smd, lai, [g1, g2, g3, g4, g5, g6], g_max, lai_max, s1) gmodel = Model(func_fit_g, independent_vars=['lai', 'kd', 'ta', 'rh', 'pa', 'smd'], param_names=['g1', 'g2', 'g3', 'g4', 'g5', 'g6']) if prms_init is None: print('Preset parameters will be loaded!') print('Please use with caution.') prms = Parameters() prm_g_0 = [3.5, 200.0, 0.13, 0.7, 30.0, 0.05] list_g = (Parameter(f'g{i+1}', prm_g_0[i], True, 0, None, None, None) for i in range(6)) prms.add_many(*list_g) # set specific bounds: # g3, g4: specific humidity related prms['g3'].set(min=0, max=1) prms['g4'].set(min=0, max=1) # g5: within reasonable temperature ranges prms['g5'].set(min=-10, max=55) # g6: within sensitive ranges of SMD prms['g6'].set(min=.02, max=.1) else: print('User provided parameters are loaded!') prms = prms_init # pack into a DataFrame for filtering out nan df_fit = pd.concat([gs_obs.rename('gs_obs'), df_obs], axis=1).dropna() res_fit = gmodel.fit( df_fit.gs_obs, kd=df_fit.kdown, ta=df_fit.Tair, rh=df_fit.RH, pa=df_fit.pres, smd=df_fit.xsmd, lai=df_fit.lai, params=prms, # useful ones: ['nelder', 'powell', 'cg', 'cobyla', 'bfgs', 'trust-tnc'] method=method, # nan_policy='omit', verbose=True, ) # provide full fitted model if debug == True otherwise only a dict with best fit parameters res = res_fit if debug else res_fit.best_values return res
result.params.pretty_print() ############################################################################### # More information about the fit is stored in the result, which is an # ``lmfit.MimimizerResult`` object (see: # https://lmfit.github.io/lmfit-py/fitting.html#lmfit.minimizer.MinimizerResult) ############################################################################### # **Specifying Bounds and Holding Parameters Constant** # # Above, the ``Model`` class implicitly builds ``Parameter`` objects from # keyword arguments of ``fit`` that match the arguments of ``decay``. You can # build the ``Parameter`` objects explicitly; the following is equivalent. result = model.fit(data, t=t, N=Parameter('N', value=10), tau=Parameter('tau', value=1)) report_fit(result.params) ############################################################################### # By building ``Parameter`` objects explicitly, you can specify bounds # (``min``, ``max``) and set parameters constant (``vary=False``). result = model.fit(data, t=t, N=Parameter('N', value=7, vary=False), tau=Parameter('tau', value=1, min=0)) report_fit(result.params) ############################################################################### # **Defining Parameters in Advance** #
def test_constraints(with_plot=True): with_plot = with_plot and WITHPLOT def residual(pars, x, sigma=None, data=None): yg = gaussian(x, pars['amp_g'].value, pars['cen_g'].value, pars['wid_g'].value) yl = lorentzian(x, pars['amp_l'].value, pars['cen_l'].value, pars['wid_l'].value) slope = pars['line_slope'].value offset = pars['line_off'].value model = yg + yl + offset + x * slope if data is None: return model if sigma is None: return (model - data) return (model - data) / sigma n = 201 xmin = 0. xmax = 20.0 x = linspace(xmin, xmax, n) data = (gaussian(x, 21, 8.1, 1.2) + lorentzian(x, 10, 9.6, 2.4) + random.normal(scale=0.23, size=n) + x * 0.5) if with_plot: pylab.plot(x, data, 'r+') pfit = [ Parameter(name='amp_g', value=10), Parameter(name='cen_g', value=9), Parameter(name='wid_g', value=1), Parameter(name='amp_tot', value=20), Parameter(name='amp_l', expr='amp_tot - amp_g'), Parameter(name='cen_l', expr='1.5+cen_g'), Parameter(name='wid_l', expr='2*wid_g'), Parameter(name='line_slope', value=0.0), Parameter(name='line_off', value=0.0) ] sigma = 0.021 # estimate of data error (for all data points) myfit = Minimizer(residual, pfit, fcn_args=(x, ), fcn_kws={ 'sigma': sigma, 'data': data }, scale_covar=True) myfit.prepare_fit() init = residual(myfit.params, x) myfit.leastsq() print(' Nfev = ', myfit.nfev) print(myfit.chisqr, myfit.redchi, myfit.nfree) report_fit(myfit.params, min_correl=0.3) fit = residual(myfit.params, x) if with_plot: pylab.plot(x, fit, 'b-') assert (myfit.params['cen_l'].value == 1.5 + myfit.params['cen_g'].value) assert (myfit.params['amp_l'].value == myfit.params['amp_tot'].value - myfit.params['amp_g'].value) assert (myfit.params['wid_l'].value == 2 * myfit.params['wid_g'].value) # now, change fit slightly and re-run myfit.params['wid_l'].expr = '1.25*wid_g' myfit.leastsq() report_fit(myfit.params, min_correl=0.4) fit2 = residual(myfit.params, x) if with_plot: pylab.plot(x, fit2, 'k') pylab.show() assert (myfit.params['cen_l'].value == 1.5 + myfit.params['cen_g'].value) assert (myfit.params['amp_l'].value == myfit.params['amp_tot'].value - myfit.params['amp_g'].value) assert (myfit.params['wid_l'].value == 1.25 * myfit.params['wid_g'].value)
return (model - data) / sigma n = 601 xmin = 0. xmax = 20.0 x = linspace(xmin, xmax, n) data = (gauss(x, 21, 8.1, 1.2) + loren(x, 10, 9.6, 2.4) + random.normal(scale=0.23, size=n) + x * 0.5) if HASPYLAB: pylab.plot(x, data, 'r+') pfit = [ Parameter(name='amp_g', value=10), Parameter(name='cen_g', value=9), Parameter(name='wid_g', value=1), Parameter(name='amp_tot', value=20), Parameter(name='amp_l', expr='amp_tot - amp_g'), Parameter(name='cen_l', expr='1.5+cen_g'), Parameter(name='wid_l', expr='2*wid_g'), Parameter(name='line_slope', value=0.0), Parameter(name='line_off', value=0.0) ] sigma = 0.021 # estimate of data error (for all data points) myfit = Minimizer(residual, pfit, fcn_args=(x, ),
"T_i_0": None }, ValueError, "was not provided in parameters, but is required.", ), ( { "T_i_1": None }, ValueError, "was not provided in parameters, but is required.", ), # If speed is not zero, vdir must be set ( { "electron_speed_0": Parameter("electron_speed_0", 1e5), "electron_vdir": None, }, ValueError, "electron_vdir must be set if electron_speeds", ), ( { "ion_speed_0": Parameter("ion_speed_0", 1e5), "ion_vdir": None }, ValueError, "ion_vdir must be set if ion_speeds", ), # Test different input types for ``ions`` ({
def calib_g( df_fc_suews, ser_ra, g_max, lai_max, wp_smd, method="cobyla", prms_init=None, debug=False, ): """Calibrate parameters for modelling surface conductance over vegetated surfaces using `LMFIT <https://lmfit.github.io/lmfit-py/model.html>`. Parameters ---------- df_fc_suews : pandas.DataFrame DataFrame in `SuPy forcing <https://supy.readthedocs.io/en/latest/data-structure/df_forcing.html>`_ format ser_ra: pandas.Series Series with RA, aerodynamic resistance, [s m-1] g_max : numeric Maximum surface conductance [mm s-1] lai_max : numeric Maximum LAI [m2 m-2] wp_smd : numeric Wilting point indicated as soil moisture deficit [mm] method: str, optional Method used in minimisation by `lmfit.minimize`: details refer to its `method<lmfit:minimize>`. prms_init: lmfit.Parameters, optional Initial parameters for calibration debug : bool, optional Option to output final calibrated `ModelResult <lmfit:ModelResult>`, by default False Returns ------- dict, or `ModelResult <lmfit:ModelResult>` if `debug==True` 1. dict: {parameter_name -> best_fit_value} 2. `ModelResult` Note: Parameters for surface conductance: g_lai (LAI related), g2 (solar radiation related), g_dq_base (humidity related), g_dq_shape (humidity related), g_ta (air temperature related), g_smd (soil moisture related) Note ---- For calibration validity, turbulent fluxes, QH and QE, in `df_fc_suews` should ONLY be observations, i.e., interpolated values should be avoided. To do so, please place `np.nan` as missing values for QH and QE. """ from lmfit import Model, Parameter, Parameters list_var_sel = ["qh", "qe", "Tair", "RH", "pres", "kdown", "xsmd", "lai"] df_obs = df_fc_suews[list_var_sel].copy().dropna() # convert to Pa df_obs.pres *= 100 gs_obs = cal_gs_obs( df_obs.qh, df_obs.qe, df_obs.Tair, df_obs.RH, df_obs.pres, ser_ra ) def func_fit_g( kd, ta, rh, pa, smd, lai, g_lai, g_kd, g_dq_base, g_dq_shape, g_ta, g_smd ): gs = cal_gs_suews( kd, ta, rh, pa, smd, lai, [g_lai, g_kd, g_dq_base, g_dq_shape, g_ta, g_smd], g_max, lai_max, wp_smd, ) return gs gmodel = Model( func_fit_g, independent_vars=["lai", "kd", "ta", "rh", "pa", "smd"], param_names=["g_lai", "g_kd", "g_dq_base", "g_dq_shape", "g_ta", "g_smd"], ) if prms_init is None: print("Preset parameters will be loaded!") print("Please use with caution.") prms = Parameters() dict_prms_init = { "lai": 3.5, "kd": 50, "q1": 0.1, "q2": 0.7, "ta": 25, "smd": 0.05, } list_g = ( Parameter(f"g_{var}", val, True, 0, None, None, None) for var, val in dict_prms_init.items() ) prms.add_many(*list_g) # set specific bounds: # g_lai: LAI related prms["g_lai"].set(min=0, max=10) prms["g_kd"].set(min=0, max=300) # g_dq_base, g_dq_shape: specific humidity related prms["g_dq_base"].set(min=0, max=1) prms["g_dq_shape"].set(min=0, max=1) # g_ta: within reasonable temperature ranges prms["g_ta"].set(min=-10, max=55) # g_smd: within sensitive ranges of SMD prms["g_smd"].set(min=0.02, max=0.1) else: print("User provided parameters are loaded!") prms = prms_init # pack into a DataFrame for filtering out nan df_fit = pd.concat([gs_obs.rename("gs_obs"), df_obs], axis=1).dropna() res_fit = gmodel.fit( df_fit.gs_obs, kd=df_fit.kdown, ta=df_fit.Tair, rh=df_fit.RH, pa=df_fit.pres, smd=df_fit.xsmd, lai=df_fit.lai, params=prms, # useful ones: ['nelder', 'powell', 'cg', 'cobyla', 'bfgs', 'trust-tnc'] method=method, # nan_policy='omit', verbose=True, ) # provide full fitted model if debug == True otherwise only a dict with best fit parameters res = res_fit if debug else res_fit.best_values return res