Curve fitting

Curve fitting in ERLabPy largely relies on lmfit. Along with some convenient models for common fitting tasks, ERLabPy provides a powerful accessor that streamlines curve fitting on multidimensional xarray objects.

ERLabPy also provides optional integration of lmfit models with iminuit, which is a Python interface to the Minuit C++ library developed at CERN.

In this tutorial, we will start with the basics of curve fitting using lmfit, introduce some models that are available in ERLabPy, and demonstrate curve fitting with the modelfit accessor to fit multidimensional xarray objects. Finally, we will show how to use iminuit with lmfit models.

Basic fitting with lmfit

[1]:
import erlab.plotting.erplot as eplt
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr
from erlab.io.exampledata import generate_data

Let’s start by defining a model function and the data to fit.

[3]:
def poly1(x, a, b):
    return a * x + b


# Generate some toy data
x = np.linspace(0, 10, 20)
y = poly1(x, 1, 2)

# Add some noise with fixed seed for reproducibility
rng = np.random.default_rng(1)
yerr = np.full_like(x, 0.5)
y = rng.normal(y, yerr)

Fitting a simple function

A lmfit model can be created by calling lmfit.Model class with the model function and the independent variable(s) as arguments.

[4]:
import lmfit

model = lmfit.Model(poly1)
params = model.make_params(a=1.0, b=2.0)
result = model.fit(y, x=x, params=params, weights=1 / yerr)

result.plot()
result
[4]:

Fit Result

Model: Model(poly1)

../_images/user-guide_curve-fitting_6_1.svg

By passing dictionaries to make_params, we can set the initial values of the parameters and also set the bounds for the parameters.

[5]:
model = lmfit.Model(poly1)
params = model.make_params(
    a={"value": 1.0, "min": 0.0},
    b={"value": 2.0, "vary": False},
)
result = model.fit(y, x=x, params=params, weights=1 / yerr)
_ = result.plot()
../_images/user-guide_curve-fitting_8_0.svg

result is a lmfit.model.ModelResult object that contains the results of the fit. The best-fit parameters can be accessed through the result.params attribute.

Note

Since all weights are the same in this case, it has little effect on the fit results. However, if we are confident that we have a good estimate of yerr, we can pass scale_covar=True to the fit method to obtain accurate uncertainties.

[6]:
result.params
[6]:
[7]:
result.params["a"].value, result.params["a"].stderr
[7]:
(0.9938903037183116, 0.011256084507121714)

The parameters can also be retrieved in a form that allows easy error propagation calculation, enabled by the uncertainties package.

[8]:
a_uvar = result.uvars["a"]
print(a_uvar)
print(a_uvar**2)
0.994+/-0.011
0.988+/-0.022

Fitting with composite models

Before fitting, let us generate a Gaussian peak on a linear background.

[9]:
# Generate toy data
x = np.linspace(0, 10, 50)
y = -0.1 * x + 2 + 3 * np.exp(-((x - 5) ** 2) / (2 * 1**2))

# Add some noise with fixed seed for reproducibility
rng = np.random.default_rng(5)
yerr = np.full_like(x, 0.3)
y = rng.normal(y, yerr)

# Plot the data
plt.errorbar(x, y, yerr, fmt="o")
[9]:
<ErrorbarContainer object of 3 artists>
../_images/user-guide_curve-fitting_15_1.svg

A composite model can be created by adding multiple models together.

[10]:
from lmfit.models import GaussianModel, LinearModel

model = GaussianModel() + LinearModel()
params = model.make_params(slope=-0.1, center=5.0, sigma={"value": 0.1, "min": 0})
params
[10]:
[11]:
result = model.fit(y, x=x, params=params, weights=1 / yerr)
result.plot()
result
[11]:

Fit Result

Model: (Model(gaussian) + Model(linear))

../_images/user-guide_curve-fitting_18_1.svg

How about multiple gaussian peaks? Since the parameter names overlap between the models, we must use the prefix argument to distinguish between them.

[12]:
model = GaussianModel(prefix="p0_") + GaussianModel(prefix="p1_") + LinearModel()
model.make_params()
[12]:

For more information, see the lmfit documentation.

Fitting with pre-defined models

Creating composite models with different prefixes every time can be cumbersome, so ERLabPy provides some pre-defined models in erlab.analysis.fit.models.

Fitting multiple peaks

One example is MultiPeakModel, which is a composite model of multiple Gaussian or Lorentzian peaks on a linear background. By supplying keyword arguments, you can specify the number of peaks, their shapes, whether to multiply with a Fermi-Dirac distribution, and whether to convolve the result with experimental resolution.

[13]:
from erlab.analysis.fit.models import MultiPeakModel

model = MultiPeakModel(npeaks=1, peak_shapes=["gaussian"], fd=False, convolve=False)
params = model.make_params(p0_center=5.0, p0_width=0.2, p0_height=3.0)
params
[13]:
[14]:
result = model.fit(y, x=x, params=params, weights=1 / yerr)
_ = result.plot()
../_images/user-guide_curve-fitting_23_0.svg

We can also plot components.

[15]:
comps = result.eval_components()
plt.errorbar(x, y, yerr, fmt="o", zorder=-1, alpha=0.3)
plt.plot(x, result.eval(), label="Best fit")
plt.plot(x, comps["1Peak_p0"], "--", label="Peak")
plt.plot(x, comps["1Peak_bkg"], "--", label="Background")
plt.legend()
[15]:
<matplotlib.legend.Legend at 0x7fd25c90a310>
../_images/user-guide_curve-fitting_25_1.svg

Now, let us try fitting MDCs cut from simulated data with multiple Lorentzian peaks, convolved with a common instrumental resolution.

[16]:
data = generate_data(bandshift=-0.2, count=5e8, seed=1).T
cut = data.qsel(ky=0.3)
cut.qplot(colorbar=True)
[16]:
<matplotlib.image.AxesImage at 0x7fd25c957350>
../_images/user-guide_curve-fitting_27_1.svg
[17]:
mdc = cut.qsel(eV=0.0)
mdc.qplot()
[17]:
[<matplotlib.lines.Line2D at 0x7fd25c5a5010>]
../_images/user-guide_curve-fitting_28_1.svg

First, we define the model and set the initial parameters.

[18]:
from erlab.analysis.fit.models import MultiPeakModel

model = MultiPeakModel(npeaks=4, peak_shapes=["lorentzian"], fd=False, convolve=True)

params = model.make_params(
    p0_center=-0.6,
    p1_center=-0.45,
    p2_center=0.45,
    p3_center=0.6,
    p0_width=0.02,
    p1_width=0.02,
    p2_width=0.02,
    p3_width=0.02,
    lin_bkg={"value": 0.0, "vary": False},
    const_bkg=0.0,
    resolution=0.03,
)
params
[18]:

Then, we can fit the model to the data:

[19]:
result = model.fit(mdc, x=mdc.kx, params=params)
result.plot()
result
[19]:

Fit Result

Model: Model(4Peak)

../_images/user-guide_curve-fitting_32_1.svg

Fitting xarray objects

ERLabPy provides accessors for xarray objects that allows you to fit data with lmfit models: xarray.DataArray.modelfit or xarray.Dataset.modelfit, depending on whether you want to fit a single DataArray or multiple DataArrays in a Dataset. The syntax is similar to xarray.DataArray.curvefit() and xarray.Dataset.curvefit(). The accessor returns a xarray.Dataset with the best-fit parameters and the fit statistics. The example below illustrates how to use the accessor to conduct the same fit as in the previous example.

[20]:
result_ds = mdc.modelfit("kx", model, params=params)
result_ds
[20]:
<xarray.Dataset> Size: 14kB
Dimensions:                (param: 23, cov_i: 23, cov_j: 23, fit_stat: 8,
                            kx: 250)
Coordinates:
    ky                     float64 8B 0.2967
    eV                     float64 8B -0.000301
  * kx                     (kx) float64 2kB -0.89 -0.8829 ... 0.8829 0.89
  * param                  (param) <U12 1kB 'p0_center' ... 'p3_amplitude'
  * fit_stat               (fit_stat) <U6 192B 'nfev' 'nvarys' ... 'aic' 'bic'
  * cov_i                  (cov_i) <U12 1kB 'p0_center' ... 'p3_amplitude'
  * cov_j                  (cov_j) <U12 1kB 'p0_center' ... 'p3_amplitude'
Data variables:
    modelfit_results       object 8B <lmfit.model.ModelResult object at 0x7fd...
    modelfit_coefficients  (param) float64 184B -0.5965 0.02403 ... 44.26
    modelfit_stderr        (param) float64 184B 2.54e-05 0.0001685 ... 0.08273
    modelfit_covariance    (cov_i, cov_j) float64 4kB 6.451e-10 ... nan
    modelfit_stats         (fit_stat) float64 64B 305.0 14.0 ... 232.5 281.8
    modelfit_data          (kx) float64 2kB 2.337 2.061 2.908 ... 3.202 4.133
    modelfit_best_fit      (kx) float64 2kB 2.336 2.437 2.545 ... 2.495 2.392

modelfit_results contains the underlying lmfit.model.ModelResult objects. The coefficients and their errors are stored in modelfit_coefficients and modelfit_stderr, respectively. The goodness-of-fit statistics are stored in modelfit_stats.

Fitting across multiple dimensions

The accessor comes in handy when you have to fit a single model to multiple data points across arbitrary dimensions. Let’s demonstrate this with a simulated cut that represents a curved Fermi edge at 100 K, with an energy broadening of 20 meV.

[21]:
from erlab.io.exampledata import generate_gold_edge
from erlab.analysis.fit.models import FermiEdgeModel

gold = generate_gold_edge(temp=100, Eres=0.02, count=5e5, seed=1)
gold.qplot(cmap="Greys")
[21]:
<matplotlib.image.AxesImage at 0x7fd257f3da50>
../_images/user-guide_curve-fitting_36_1.svg

We first select ± 0.2 eV around the Fermi level and fit the model across the energy axis for every EDC.

[22]:
gold_selected = gold.sel(eV=slice(-0.2, 0.2))
result_ds = gold_selected.modelfit(
    coords="eV",
    model=FermiEdgeModel(),
    params={"temp": {"value": 100.0, "vary": False}},
    guess=True,
)
result_ds
[22]:
<xarray.Dataset> Size: 358kB
Dimensions:                (alpha: 200, param: 7, cov_i: 7, cov_j: 7,
                            fit_stat: 8, eV: 75)
Coordinates:
  * alpha                  (alpha) float64 2kB -15.0 -14.85 -14.7 ... 14.85 15.0
  * eV                     (eV) float64 600B -0.1977 -0.1923 ... 0.193 0.1983
  * param                  (param) <U10 280B 'center' 'temp' ... 'dos0' 'dos1'
  * fit_stat               (fit_stat) <U6 192B 'nfev' 'nvarys' ... 'aic' 'bic'
  * cov_i                  (cov_i) <U10 280B 'center' 'temp' ... 'dos0' 'dos1'
  * cov_j                  (cov_j) <U10 280B 'center' 'temp' ... 'dos0' 'dos1'
Data variables:
    modelfit_results       (alpha) object 2kB <lmfit.model.ModelResult object...
    modelfit_coefficients  (alpha, param) float64 11kB -0.01774 100.0 ... -3.53
    modelfit_stderr        (alpha, param) float64 11kB 0.005598 0.0 ... 4.111
    modelfit_covariance    (alpha, cov_i, cov_j) float64 78kB 3.134e-05 ... 16.9
    modelfit_stats         (alpha, fit_stat) float64 13kB 106.0 6.0 ... -26.34
    modelfit_data          (alpha, eV) float64 120kB 5.1 7.021 7.453 ... 0.0 0.0
    modelfit_best_fit      (alpha, eV) float64 120kB 6.642 6.565 ... -0.0711

Let’s plot the fitted parameters as a function of angle!

[23]:
gold.qplot(cmap="Greys")
plt.errorbar(
    gold_selected.alpha,
    result_ds.modelfit_coefficients.sel(param="center"),
    result_ds.modelfit_stderr.sel(param="center"),
    fmt=".",
)
[23]:
<ErrorbarContainer object of 3 artists>
../_images/user-guide_curve-fitting_40_1.svg

We can also conduct a global fit to all the EDCs with a multidimensional model. First, we normalize the data with the averaged intensity of each EDC and then fit the data to FermiEdge2dModel.

[24]:
from erlab.analysis.fit.models import FermiEdge2dModel

gold_norm = gold_selected / gold_selected.mean("eV")
result_ds = gold_norm.T.modelfit(
    coords=["eV", "alpha"],
    model=FermiEdge2dModel(),
    params={"temp": {"value": 100.0, "vary": False}},
    guess=True,
)
result_ds
[24]:
<xarray.Dataset> Size: 244kB
Dimensions:                (param: 8, cov_i: 8, cov_j: 8, fit_stat: 8, eV: 75,
                            alpha: 200)
Coordinates:
  * eV                     (eV) float64 600B -0.1977 -0.1923 ... 0.193 0.1983
  * alpha                  (alpha) float64 2kB -15.0 -14.85 -14.7 ... 14.85 15.0
  * param                  (param) <U10 320B 'c0' 'c1' ... 'offset' 'resolution'
  * fit_stat               (fit_stat) <U6 192B 'nfev' 'nvarys' ... 'aic' 'bic'
  * cov_i                  (cov_i) <U10 320B 'c0' 'c1' ... 'offset' 'resolution'
  * cov_j                  (cov_j) <U10 320B 'c0' 'c1' ... 'offset' 'resolution'
Data variables:
    modelfit_results       object 8B <lmfit.model.ModelResult object at 0x7fd...
    modelfit_coefficients  (param) float64 64B 0.03604 8.09e-06 ... 0.04345
    modelfit_stderr        (param) float64 64B 0.0004463 2.749e-05 ... 0.001243
    modelfit_covariance    (cov_i, cov_j) float64 512B 1.992e-07 ... 1.545e-06
    modelfit_stats         (fit_stat) float64 64B 82.0 7.0 ... -3.98e+04
    modelfit_data          (eV, alpha) float64 120kB 2.027 1.905 ... 0.0 0.0
    modelfit_best_fit      (eV, alpha) float64 120kB 1.965 1.965 ... 0.02488

Let’s plot the fit results and the residuals.

[25]:
best_fit = result_ds.modelfit_best_fit.transpose(*gold_norm.dims)

fig, axs = eplt.plot_slices(
    [gold_norm, best_fit, best_fit - gold_norm],
    figsize=(4, 5),
    cmap=["viridis", "viridis", "bwr"],
    norm=[plt.Normalize(), plt.Normalize(), eplt.CenteredPowerNorm(1.0, vcenter=0)],
    colorbar="all",
    hide_colorbar_ticks=False,
    colorbar_kw={"width": 7},
)
eplt.set_titles(axs, ["Data", "FermiEdge2dModel", "Residuals"])
../_images/user-guide_curve-fitting_45_0.svg

Providing multiple initial guesses

You can also provide different initial guesses and bounds for different coordinates. To demonstrate, let’s create some data containing multiple MDCs, each with a different peak position.

[26]:
# Define angle coordinates for 2D data
alpha = np.linspace(-5.0, 5.0, 100)
beta = np.linspace(-1.0, 1.0, 3)

# Center of the peaks along beta
center = np.array([-2.0, 0.0, 2.0])[:, np.newaxis]

# Gaussian peak on a linear background
y = -0.1 * alpha + 2 + 3 * np.exp(-((alpha - center) ** 2) / (2 * 1**2))

# Add some noise with fixed seed for reproducibility
rng = np.random.default_rng(5)
yerr = np.full_like(y, 0.1)
y = rng.normal(y, yerr)

# Transform to DataArray
darr = xr.DataArray(y, dims=["beta", "alpha"], coords={"beta": beta, "alpha": alpha})
darr.qplot()
[26]:
<matplotlib.image.AxesImage at 0x7fd256e05a50>
../_images/user-guide_curve-fitting_47_1.svg

We can provide different initial guesses for the peak positions along the beta dimension by passing a dictionary of DataArrays to the params argument.

[27]:
result_ds = darr.modelfit(
    coords="alpha",
    model=GaussianModel() + LinearModel(),
    params={
        "center": xr.DataArray([-2, 0, 2], coords=[darr.beta]),
        "slope": -0.1,
    },
)
result_ds
[27]:
<xarray.Dataset> Size: 7kB
Dimensions:                (beta: 3, param: 5, cov_i: 5, cov_j: 5, fit_stat: 8,
                            alpha: 100)
Coordinates:
  * beta                   (beta) float64 24B -1.0 0.0 1.0
  * alpha                  (alpha) float64 800B -5.0 -4.899 -4.798 ... 4.899 5.0
  * param                  (param) <U9 180B 'amplitude' 'center' ... 'intercept'
  * fit_stat               (fit_stat) <U6 192B 'nfev' 'nvarys' ... 'aic' 'bic'
  * cov_i                  (cov_i) <U9 180B 'amplitude' 'center' ... 'intercept'
  * cov_j                  (cov_j) <U9 180B 'amplitude' 'center' ... 'intercept'
Data variables:
    modelfit_results       (beta) object 24B <lmfit.model.ModelResult object ...
    modelfit_coefficients  (beta, param) float64 120B 7.324 -2.0 ... 1.993
    modelfit_stderr        (beta, param) float64 120B 0.1349 0.011 ... 0.01735
    modelfit_covariance    (beta, cov_i, cov_j) float64 600B 0.01819 ... 0.00...
    modelfit_stats         (beta, fit_stat) float64 192B 31.0 5.0 ... -450.2
    modelfit_data          (beta, alpha) float64 2kB 2.453 2.402 ... 1.404 1.64
    modelfit_best_fit      (beta, alpha) float64 2kB 2.553 2.553 ... 1.526 1.504

Let’s overlay the fitted peak positions on the data.

[28]:
result_ds.modelfit_data.qplot()
result_center = result_ds.sel(param="center")
plt.plot(result_center.modelfit_coefficients, result_center.beta, '.-')
[28]:
[<matplotlib.lines.Line2D at 0x7fd257e21610>]
../_images/user-guide_curve-fitting_51_1.svg

The same can be done with all parameter attributes that can be passed to lmfit.create_params() (e.g., vary, min, max, etc.). For example:

[29]:
result_ds = darr.modelfit(
    coords="alpha",
    model=GaussianModel() + LinearModel(),
    params={
        "center": {
            "value": xr.DataArray([-2, 0, 2], coords=[darr.beta]),
            "min": -5.0,
            "max": xr.DataArray([0, 2, 5], coords=[darr.beta]),
        },
        "slope": -0.1,
    },
)
result_ds
[29]:
<xarray.Dataset> Size: 7kB
Dimensions:                (beta: 3, param: 5, cov_i: 5, cov_j: 5, fit_stat: 8,
                            alpha: 100)
Coordinates:
  * beta                   (beta) float64 24B -1.0 0.0 1.0
  * alpha                  (alpha) float64 800B -5.0 -4.899 -4.798 ... 4.899 5.0
  * param                  (param) <U9 180B 'amplitude' 'center' ... 'intercept'
  * fit_stat               (fit_stat) <U6 192B 'nfev' 'nvarys' ... 'aic' 'bic'
  * cov_i                  (cov_i) <U9 180B 'amplitude' 'center' ... 'intercept'
  * cov_j                  (cov_j) <U9 180B 'amplitude' 'center' ... 'intercept'
Data variables:
    modelfit_results       (beta) object 24B <lmfit.model.ModelResult object ...
    modelfit_coefficients  (beta, param) float64 120B 7.324 -2.0 ... 1.993
    modelfit_stderr        (beta, param) float64 120B 0.1349 0.011 ... 0.01735
    modelfit_covariance    (beta, cov_i, cov_j) float64 600B 0.01819 ... 0.00...
    modelfit_stats         (beta, fit_stat) float64 192B 31.0 5.0 ... -450.2
    modelfit_data          (beta, alpha) float64 2kB 2.453 2.402 ... 1.404 1.64
    modelfit_best_fit      (beta, alpha) float64 2kB 2.553 2.553 ... 1.526 1.504

Visualizing fits

If hvplot is installed, we can visualize the fit results interactively with the qshow accessor.

Note

If you are viewing this documentation online, the plot will not be interactive. Run the code locally to try it out.

[30]:
result_ds.qshow(plot_components=True)
[30]:

Note

There is a dedicated module for Fermi edge fitting and correction, described here.

Parallelization

The accessors are tightly integrated with xarray, so passing a dask array will parallelize the fitting process. See Parallel Computing with Dask for more information.

For non-dask objects, you can achieve joblib-based parallelization:

  • For non-dask Datasets, basic parallelization across multiple data variables can be achieved with the parallel argument to xarray.Dataset.modelfit.

  • For parallelizing fits on a single DataArray along a dimension with many points, the xarray.DataArray.parallel_fit accessor can be used. This method is similar to xarray.DataArray.modelfit, but requires the name of the dimension to parallelize over instead of the coordinates to fit along. For example, to parallelize the fit in the previous example, you can use the following code:

    gold_selected.parallel_fit(
        dim="alpha",
        model=FermiEdgeModel(),
        params={"temp": {"value": 100.0, "vary": False}},
        guess=True,
    )
    

    Note

    • Note that the initial run will take a long time due to the overhead of creating parallel workers. Subsequent calls will run faster, since joblib’s default backend will try to reuse the workers.

    • The accessor has some intrinsic overhead due to post-processing. If you need the best performance, handle the parallelization yourself with joblib and lmfit.Model.fit.

Saving and loading fits

Since the resulting dataset contains no Python objects, it can be saved and loaded reliably as a netCDF file using xarray.Dataset.to_netcdf() and xarray.open_dataset(), respectively. If you wish to obtain the lmfit.model.ModelResult objects from the fit, you can use the output_result argument.

Warning

Saving full model results that includes the model functions can be difficult. Instead of saving the fit results, it is recommended to save the code that can reproduce the fit. See the relevant lmfit documentation for more information.

Fermi edge fitting

Functions related to the Fermi edge are available in erlab.analysis.gold. To fit a polynomial to a Fermi edge, you can use erlab.analysis.gold.poly().

[32]:
import erlab.analysis as era
import erlab.plotting.erplot as eplt

gold = generate_gold_edge(temp=100, seed=1)
result = era.gold.poly(
    gold,
    angle_range=(-15, 15),
    eV_range=(-0.2, 0.2),
    temp=100.0,
    vary_temp=False,
    degree=2,
    plot=True,
)
../_images/user-guide_curve-fitting_58_1.svg

The resulting polynomial can be used to correct the Fermi edge with erlab.analysis.gold.correct_with_edge().

[33]:
era.correct_with_edge(gold, result).qplot(cmap="Greys")
eplt.fermiline()
[33]:
<matplotlib.lines.Line2D at 0x7fd254a15810>
../_images/user-guide_curve-fitting_60_1.svg

Also check out the interactive Fermi edge fitting tool, erlab.interactive.goldtool().

Using iminuit

Note

This part requires iminuit.

iminuit is a powerful Python interface to the Minuit C++ library developed at CERN. To learn more, see the iminuit documentation.

ERLabPy provides a thin wrapper around iminuit.Minuit that allows you to use lmfit models with iminuit. The example below conducts the same fit as the previous one, but using iminuit.

[34]:
from erlab.analysis.fit.minuit import Minuit
from erlab.analysis.fit.models import MultiPeakModel

model = MultiPeakModel(npeaks=4, peak_shapes=["lorentzian"], fd=False, convolve=True)

m = Minuit.from_lmfit(
    model,
    mdc,
    mdc.kx,
    p0_center=-0.6,
    p1_center=-0.45,
    p2_center=0.45,
    p3_center=0.6,
    p0_width=0.02,
    p1_width=0.02,
    p2_width=0.02,
    p3_width=0.02,
    p0_height=1500,
    p1_height=50,
    p2_height=50,
    p3_height=1500,
    lin_bkg={"value": 0.0, "vary": False},
    const_bkg=0.0,
    resolution=0.03,
)

m.migrad()
m.minos()
m.hesse()
[34]:
Migrad
FCN = 566.5 (χ²/ndof = 2.4) Nfcn = 5120
EDM = 4.13e-06 (Goal: 0.0002) time = 0.9 sec
Valid Minimum Below EDM threshold (goal x 10)
No parameters at limit Below call limit
Hesse ok Covariance accurate
Name Value Hesse Error Minos Error- Minos Error+ Limit- Limit+ Fixed
0 p0_center -596.505e-3 0.016e-3 -0.016e-3 0.016e-3
1 p0_width 24.03e-3 0.11e-3 -0.11e-3 0.11e-3 0
2 p0_height 1.161e3 0.004e3 -0.004e3 0.004e3 0
3 p1_center -444.89e-3 0.30e-3 -0.30e-3 0.30e-3
4 p1_width 18e-3 1e-3 -1e-3 1e-3 0
5 p1_height 68.5 2.8 -2.7 2.9 0
6 p2_center 443.53e-3 0.34e-3 -0.34e-3 0.34e-3
7 p2_width 0.0237 0.0010 -0.0010 0.0010 0
8 p2_height 57.1 1.8 -1.7 1.8 0
9 p3_center 596.065e-3 0.016e-3 -0.016e-3 0.017e-3
10 p3_width 24.44e-3 0.11e-3 -0.11e-3 0.11e-3 0
11 p3_height 1.153e3 0.004e3 -0.004e3 0.004e3 0
12 lin_bkg 0.0 0.1 yes
13 const_bkg 0.27 0.09 -0.09 0.09
14 resolution 26.34e-3 0.10e-3 -0.10e-3 0.10e-3 0
p0_center p0_width p0_height p1_center p1_width p1_height p2_center p2_width p2_height p3_center p3_width p3_height const_bkg resolution
Error -0.016e-3 0.016e-3 -0.11e-3 0.11e-3 -4 4 -0.3e-3 0.3e-3 -1e-3 1e-3 -2.7 2.9 -0.34e-3 0.34e-3 -0.001 0.001 -1.7 1.8 -0.016e-3 0.017e-3 -0.11e-3 0.11e-3 -4 4 -0.09 0.09 -0.1e-3 0.1e-3
Valid True True True True True True True True True True True True True True True True True True True True True True True True True True True True
At Limit False False False False False False False False False False False False False False False False False False False False False False False False False False False False
Max FCN False False False False False False False False False False False False False False False False False False False False False False False False False False False False
New Min False False False False False False False False False False False False False False False False False False False False False False False False False False False False
p0_center p0_width p0_height p1_center p1_width p1_height p2_center p2_width p2_height p3_center p3_width p3_height lin_bkg const_bkg resolution
p0_center 2.68e-10 0.01e-9 (0.004) -309.64e-9 (-0.005) 0.02e-9 (0.004) -0.47e-9 (-0.029) 1.09497e-6 (0.024) -0 -0.04e-9 (-0.002) 24.04e-9 -0 0.01e-9 (0.004) -283.08e-9 (-0.004) 0 9.90e-9 (0.007) -0.01e-9 (-0.005)
p0_width 0.01e-9 (0.004) 1.17e-08 -442.668e-6 (-0.981) 0.001e-6 (0.036) 0.001e-6 (0.007) -10.032e-6 (-0.033) -0.002e-6 (-0.045) 0.006e-6 (0.054) -13.035e-6 (-0.068) 0.01e-9 (0.008) 0.009e-6 (0.785) -352.815e-6 (-0.801) 0 -3.894e-6 (-0.419) -0.010e-6 (-0.879)
p0_height -309.64e-9 (-0.005) -442.668e-6 (-0.981) 17.4 -41.28e-6 (-0.033) -63.7e-6 (-0.015) 1 (0.043) 66.39e-6 (0.047) -173.7e-6 (-0.040) 0.5 (0.062) -523.22e-9 (-0.008) -362.570e-6 (-0.802) 14 (0.823) 0 0.132 (0.369) 394.794e-6 (0.906)
p1_center 0.02e-9 (0.004) 0.001e-6 (0.036) -41.28e-6 (-0.033) 8.75e-08 0 (0.016) -7.69e-6 (-0.009) -0 (-0.001) 0 (0.004) -1.73e-6 (-0.003) 0 0.001e-6 (0.026) -31.38e-6 (-0.026) 0 -0.59e-6 (-0.023) -0.001e-6 (-0.028)
p1_width -0.47e-9 (-0.029) 0.001e-6 (0.007) -63.7e-6 (-0.015) 0 (0.016) 9.86e-07 -2.6249e-3 (-0.949) -0 0.1e-6 (0.057) -53.5e-6 (-0.030) 0.04e-9 (0.003) 0.007e-6 (0.067) -212.3e-6 (-0.053) 0e-6 -20.8e-6 (-0.244) -0.005e-6 (-0.045)
p1_height 1.09497e-6 (0.024) -10.032e-6 (-0.033) 1 (0.043) -7.69e-6 (-0.009) -2.6249e-3 (-0.949) 7.76 2.16e-6 (0.002) -111.5e-6 (-0.039) 0.1 (0.023) -98.11e-9 (-0.002) -24.143e-6 (-0.080) 1 (0.072) 0 0.042 (0.177) 20.462e-6 (0.070)
p2_center -0 -0.002e-6 (-0.045) 66.39e-6 (0.047) -0 (-0.001) -0 2.16e-6 (0.002) 1.15e-07 0.03e-6 (0.084) -44.92e-6 (-0.075) 0.05e-9 (0.009) -0.002e-6 (-0.065) 83.33e-6 (0.060) 0 0.30e-6 (0.010) 0.002e-6 (0.052)
p2_width -0.04e-9 (-0.002) 0.006e-6 (0.054) -173.7e-6 (-0.040) 0 (0.004) 0.1e-6 (0.057) -111.5e-6 (-0.039) 0.03e-6 (0.084) 1.07e-06 -1.6728e-3 (-0.911) 0.57e-9 (0.033) -0.001e-6 (-0.011) -0.7e-6 0 -20.8e-6 (-0.234) -0.003e-6 (-0.031)
p2_height 24.04e-9 -13.035e-6 (-0.068) 0.5 (0.062) -1.73e-6 (-0.003) -53.5e-6 (-0.030) 0.1 (0.023) -44.92e-6 (-0.075) -1.6728e-3 (-0.911) 3.15 -752.74e-9 (-0.026) -4.459e-6 (-0.023) 0.3 (0.036) 0.0 0.021 (0.136) 11.425e-6 (0.062)
p3_center -0 0.01e-9 (0.008) -523.22e-9 (-0.008) 0 0.04e-9 (0.003) -98.11e-9 (-0.002) 0.05e-9 (0.009) 0.57e-9 (0.033) -752.74e-9 (-0.026) 2.71e-10 0.01e-9 (0.005) -373.21e-9 (-0.006) 0 -16.96e-9 (-0.012) -0.01e-9 (-0.008)
p3_width 0.01e-9 (0.004) 0.009e-6 (0.785) -362.570e-6 (-0.802) 0.001e-6 (0.026) 0.007e-6 (0.067) -24.143e-6 (-0.080) -0.002e-6 (-0.065) -0.001e-6 (-0.011) -4.459e-6 (-0.023) 0.01e-9 (0.005) 1.18e-08 -432.171e-6 (-0.980) 0 -3.878e-6 (-0.417) -0.010e-6 (-0.879)
p3_height -283.08e-9 (-0.004) -352.815e-6 (-0.801) 14 (0.823) -31.38e-6 (-0.026) -212.3e-6 (-0.053) 1 (0.072) 83.33e-6 (0.060) -0.7e-6 0.3 (0.036) -373.21e-9 (-0.006) -432.171e-6 (-0.980) 16.5 0 0.128 (0.367) 384.873e-6 (0.905)
lin_bkg 0 0 0 0 0e-6 0 0 0 0.0 0 0 0 0 0.000 0
const_bkg 9.90e-9 (0.007) -3.894e-6 (-0.419) 0.132 (0.369) -0.59e-6 (-0.023) -20.8e-6 (-0.244) 0.042 (0.177) 0.30e-6 (0.010) -20.8e-6 (-0.234) 0.021 (0.136) -16.96e-9 (-0.012) -3.878e-6 (-0.417) 0.128 (0.367) 0.000 0.00736 3.146e-6 (0.351)
resolution -0.01e-9 (-0.005) -0.010e-6 (-0.879) 394.794e-6 (0.906) -0.001e-6 (-0.028) -0.005e-6 (-0.045) 20.462e-6 (0.070) 0.002e-6 (0.052) -0.003e-6 (-0.031) 11.425e-6 (0.062) -0.01e-9 (-0.008) -0.010e-6 (-0.879) 384.873e-6 (0.905) 0 3.146e-6 (0.351) 1.09e-08
../_images/user-guide_curve-fitting_62_1.svg

You can also use the interactive fitting interface provided by iminuit.

Note

  • Requires ipywidgets to be installed.

  • If you are viewing this documentation online, changing the sliders won’t change the plot. run the code locally to try it out.

[35]:
m.interactive()
[35]: