Hello all,
I have a code on my local machine that gives me x,y values from a set of parameters and the energy. For a single energy, I wrote a black box with Model that can fit a single dataset. It worked great!
I'm currently trying to build up to an energy-dependent model, which means I have to fit several datasets at once with different energies but the same set of parameters. Based on several of the posted examples, I found that I could get it to work with the minimize() function, but I lose the functionality of the ModelResult class. I would really like to recover the eval() and eval_uncertainties() functions so I can plot uncertainty bands. Is there a clever way I'm not seeing to move back to Model?
Here's a streamlined example of my minimize() code, and the single-dataset version with model() below.
Many thanks!
Andrea
import lmfit
import numpy as np
from scipy.interpolate import CubicSpline
import data_wrapper # user defined code, black box
import run # user defined code, black box
def modelfit(x, energy,parameters,parameter_labels):
model_x, model_y = run.run(energy,parameters,parameter_labels) # returns a list of xs, ys
spl = CubicSpline(model_x,model_y)
return spl(x) # match y values to input x values
def get_pars(datasets, initial_guesses, parameter_labels, rangemin, rangemax):
fit_params = lmfit.Parameters()
for parameter_index, parameter in enumerate(parameter_labels):
myval = np.array(initial_guesses)[parameter_index]
fit_params.add(f'{parameter}', value=myval, min=rangemin[parameter_index], max=rangemax[parameter_index])
return fit_params
def objective(parameters, datasets, parameter_labels):
resid = []
for dataset_index, dataset in enumerate(datasets):
input_parameters = []
parameter_indices = list(parameters.keys())
for parameter_index, parameter in enumerate(parameters):
input_parameters.append(parameters[parameter_indices[parameter_index]].value)
model_y = modelfit(dataset.x, dataset.energy, input_parameters, parameter_labels)
resid.extend((dataset.y = model.y)/dataset.errs) # append and flatten residuals simultaneously
return resid
parameter_labels = ["V","R","A"] # shared by all datasets
rangemin = [0,1,2]
rangemax = [3,4,5]
initial_guesses = run.lookup_literature_guesses(E, parameter_labels) # list of floats
datasets = data_wrapper.get_datasets() # array of objects
parameters = get_pars(datasets, initial_guesses, parameter_labels, rangemin, rangemax)
output = lmfit.minimize(objective, parameters, args=(datasets, parameter_labels))
lmfit.report_fit(output.params)
###############################################################################
# single-dataset version with Model
import matplotlib.pyplot as plt
def modelfit2(x, V, R, A, energy):
pars = [V,R,A]
parlabs = ["V","R","A"]
model_x, model_y = run.run( energy, pars, parlabs)
spl = CubicSpline(model_x, model_y)
def evaluate_model(y,x,errs,parameters,rangemin, rangemax, energy):
model = lmfit.Model(modelfit2)
pars = mod.make_params(
V={'value':parameters[0],'min':rangemin[0],'max':rangemax[0]},
R={'value':parameters[1],'min':rangemin[1],'max':rangemax[1]},
A={'value':parameters[2],'min':rangemin[2],'max':rangemax[2]},
energy={'value':energy, 'vary':False}
)
return model.fit(y, pars, x=x, method='leastsq', weights=1/errs)
ds = datasets[0]
result = evaluate_model(ds.y, ds.x, ds.errs, initial_guesses, ds.energy)
xfine = ds.x
yfine = result.eval(t=ds.x)
efine = result.eval_uncertainty(t=xfine,sigma=1)
fitrange = plt.fill_between(xfine,yfine-efine, yfine+efine)