Minimizer

BornAgain fitting uses standard Python minimization packages. The recommended choice is lmfit, which provides a convenient parameter interface and several algorithms. For global search (useful when parameters are far from their optimal values), scipy.optimize.differential_evolution is a good option.

Fit parameter setup with lmfit

The lmfit.Parameters class defines a collection of fit parameters. Each parameter has a unique name, starting value, and optional bounds.

import lmfit
P = lmfit.Parameters()
P.add("radius",  value=5*nm, min=1*nm, max=10*nm)
P.add("length",  value=10*nm, min=8*nm, max=14*nm)
P.add("density", value=1e-4, vary=False)

Running the fit

Pass the FitObjective residual function and the parameters to lmfit.minimize:

fit_objective = ba.FitObjective()
fit_objective.addFitPair(run_simulation, exp_data)
fit_objective.initPrint(10)

result = lmfit.minimize(fit_objective.evaluate_residuals, P)
print(lmfit.fit_report(result))

Two-stage fit (global + local)

For difficult problems, combine a global search with a local refinement:

import scipy

# Stage 1: global search
bounds = [(1*nm, 10*nm), (8*nm, 14*nm)]
result1 = scipy.optimize.differential_evolution(
    fit_objective.evaluate, bounds, seed=42, tol=1e-4, maxiter=100)

# Stage 2: local refinement seeded from stage 1
P["radius"].value = result1.x[0]
P["length"].value = result1.x[1]
result2 = lmfit.minimize(fit_objective.evaluate_residuals, P)
print(lmfit.fit_report(result2))

Further resources