lbl-anp / becquerel

Becquerel is a Python package for analyzing nuclear spectroscopic measurements.
Other
43 stars 16 forks source link

Fitting two peaks should return params in a well-defined order #302

Open jvavrek opened 2 years ago

jvavrek commented 2 years ago

I'm currently getting something like

>>> fitter.best_values

{...
'gauss1_mu': 1345,
...
'gauss0_mu': 1461
}

We should use parameter names in ascending order of energy:

>>> fitter.best_values

{...
'gauss0_mu': 1345,
...
'gauss1_mu': 1461
}
markbandstra commented 2 years ago

Do you think this sorting is best done after the fitting has been performed (e.g., renaming the parameter names based on their order), or encoded in the parameter constraints before fitting? For the latter the documentation says we would have to introduce an auxiliary variable, like this:

model.add("delta_mu_01", min=5.0, vary=True)
model.set_param_hint("gauss1_mu", expr="gauss0_mu + delta_mu_01")

Maybe this could be done in Fitter._make_model?

jvavrek commented 2 years ago

Do you think this sorting is best done after the fitting has been performed (e.g., renaming the parameter names based on their order), or encoded in the parameter constraints before fitting? For the latter the documentation says we would have to introduce an auxiliary variable, like this:

model.add("delta_mu_01", min=5.0, vary=True)
model.set_param_hint("gauss1_mu", expr="gauss0_mu + delta_mu_01")

Maybe this could be done in Fitter._make_model?

Renaming after the fit was the first thing I thought of, but I imagine that could break all kinds of internal data. Probably best to introduce that auxiliary variable. Nice find!

markbandstra commented 2 years ago

Renaming after the fit was the first thing I thought of, but I imagine that could break all kinds of internal data. Probably best to introduce that auxiliary variable. Nice find!

If that approach works well, I think it would be a cleaner way to do it -- e.g., (I don't know for sure) but renaming could mess with the covariance matrix. I guess we could check for multiple gauss*_mu and add some logic (in case there are three). Are there other model types we would want to sort like this?

jvavrek commented 2 years ago

If that approach works well, I think it would be a cleaner way to do it -- e.g., (I don't know for sure) but renaming could mess with the covariance matrix. I guess we could check for multiple gauss*_mu and add some logic (in case there are three). Are there other model types we would want to sort like this?

We should probably handle multiple expgauss as well. Or any other peak variants, or combinations thereof (e.g. one "gauss", one "expgauss").

jvavrek commented 2 years ago

In the meantime, one can use the limits kwarg to specify non-overlapping ranges, at least with backend="iminuit-pml":

fitter.fit(
    backend="iminuit-pml",
    limits={
        "gauss0_mu": (1300, 1400),
        "gauss1_mu": (1500, 1600),
    }
)