Asymmetric models generation ?

423 views
Skip to first unread message

peja...@gmail.com

unread,
Aug 21, 2017, 1:47:41 PM8/21/17
to lmfit-py
I am still discovering the wonders of lmfit (and python...) I therefore apologise if my question is trivial or ill-formulated.

I would like to make a function that would take any model (e.g. PseudoVoigt) and create an asymmetric version of it, to be able to test different profiles to fit asymmetric X-ray diffraction peaks.

Creating a function generating a piecewise model using different prefixes (e.g. prefix='Left_' and prefix='Right_') through if/else is not a problem, but the two points that block me are : 
1. Some of the parameters are shared between the two sides (e.g. the position and maximum of the maximum) but others are side-dependent (generally width, but potentially mixing parameters in case of a pseudo-voigt for example). The number of shared and side-dependent parameters depends on the model and, potentially on the user.
2. I would like this function to be able to handle any model (i.e. having a variable number of parameters).

So my questions are : 
Q1. Is there a way to create such a universal "model-splitting" function ?
Q2. If no, then how can I create it for a given model, say a PseudoVoigt ? (I should then be able to derive similar scheme for the other models)

Thanks a lot !

Pierre

Matt Newville

unread,
Aug 23, 2017, 10:39:00 PM8/23/17
to lmfit-py
Hi Pierre,

Sorry for the delay, I was out enjoying the wonders of nature and the solar system.

On Mon, Aug 21, 2017 at 12:47 PM, <peja...@gmail.com> wrote:
I am still discovering the wonders of lmfit (and python...) I therefore apologise if my question is trivial or ill-formulated.

I would like to make a function that would take any model (e.g. PseudoVoigt) and create an asymmetric version of it, to be able to test different profiles to fit asymmetric X-ray diffraction peaks.

Creating a function generating a piecewise model using different prefixes (e.g. prefix='Left_' and prefix='Right_') through if/else is not a problem, but the two points that block me are : 
1. Some of the parameters are shared between the two sides (e.g. the position and maximum of the maximum) but others are side-dependent (generally width, but potentially mixing parameters in case of a pseudo-voigt for example). The number of shared and side-dependent parameters depends on the model and, potentially on the user.

I think that should be doable...

2. I would like this function to be able to handle any model (i.e. having a variable number of parameters).

So my questions are : 
Q1. Is there a way to create such a universal "model-splitting" function ?

Maybe you could pass in the names of the functions to use?

Q2. If no, then how can I create it for a given model, say a PseudoVoigt ? (I should then be able to derive similar scheme for the other models)


Well, you could probably do something like this (using a simplified Gaussian to make for a simple example):

    def gauss(x, amp=1.0, cen=0.0, sigma=1.0):
        """a 1-dimensional Gaussian function."""
        return  amp*np.exp(-(x-cen)**2 / (2*sigma**2))

    def asympeak(x, amp=1.0, cen=0.0, sigma=1.0):
        """gaussian with width of sigma x< center, width of 3sigma for x>center"""
        f = gauss(x, amp=amp, cen=cen, sigma=sigma)
        f[np.where(x > cen)] = gauss(x[np.where(x>cen)], amp=amp, cen=cen, sigma=3*sigma)
        return f

    gmodel = Model(asympeak)
    result = gmodel.fit(y, x=x, amp=5, cen=5, wid=1)

But that might a little too restrictive for what you want.

Another approach might be to define an asymmetric function that multiplies the peak model, along the lines of http://lmfit.github.io/lmfit-py/builtin_models.html?highlight=skew#lmfit.models.SkewedGaussianModel

In fact, there is a skewed Voigt function in lineshapes.py (skewed_voigt(), see https://github.com/lmfit/lmfit-py/blob/master/lmfit/lineshapes.py#L230), though it's not exposed as a builtin Model -- that could be fixed easily. 

Finally,  because Models can be not only added but also multiplied, you could define an asymmetry model:

    def asym(x,  center=0, beta=1):
        return 1 + scipy.special.erf(beta*(x-center))

and then combine that with a VoigtModel:

    asym_voigt = VoigtModel() * Model(asym)


That might be the most general approach.  If that sounds like a good approach to you, we could try to add an AsymmetryModel (and/or perhaps one using a different functional form?)

Hope that helps, or at least is a good start...

--Matt

peja...@gmail.com

unread,
Aug 24, 2017, 6:49:09 AM8/24/17
to lmfit-py
Hi Matt, 
Thanks for the answer. I hope you enjoyed the eclipse. You're lucky guy, the next total eclipse in the US is in not so long ! 

I had been fidgeting with something that looked like you're first solution, but I could not make it work. Thank you for providing me with a solution !

The skewed functions are indeed a good solution, thanks for pointing them out to me. As a lazy physicist, as soon as I saw the profiles I needed for my purpose, I dove into using them and not being a native speaker (nor very versed in statistics) "skewed" did not ring a bell. 

Just to be sure, I'd have a question about your skewed_gaussian : your "beta" corresponds to wikipedia's "alpha" and you introduce a new parameter "gamma" that enters the definition of the asymmetry and may be subsequently fitted. Is that correct ? As I was thinking about the Lorentzian and Pseudo-Voigt profiles, I was starting to mix up the "gamma" for the asymmetry and the one related to the HWHM of the Lorentzian profile. 

In order to be coherent with your site, it might be good to have skewed versions of the Built-in models (from a selfish point of view, I would particularly be interested in a skewed Lorentzian, pseudo-voigt and Pearson VII, as they are quite useful for diffraction data) as well as an "asymmetry model" (also based on the same form) for models built by the user. 

Concerning the functional to use to introduce the asymmetry, I am not aware of any particular preference for elastic scattering data. I have never come across anything but the definition a piecewise function with two hwhm's, which usually brings in more problems than solutions.

Let me know how I can help. 

-- Pierre

Matt Newville

unread,
Aug 24, 2017, 11:22:06 PM8/24/17
to lmfit-py
Hi Pierre,

On Thu, Aug 24, 2017 at 5:49 AM, <peja...@gmail.com> wrote:
Hi Matt, 
Thanks for the answer. I hope you enjoyed the eclipse. You're lucky guy, the next total eclipse in the US is in not so long ! 

I had been fidgeting with something that looked like you're first solution, but I could not make it work. Thank you for providing me with a solution !

The skewed functions are indeed a good solution, thanks for pointing them out to me. As a lazy physicist, as soon as I saw the profiles I needed for my purpose, I dove into using them and not being a native speaker (nor very versed in statistics) "skewed" did not ring a bell. 


I'm a lazy physicist myself!


Just to be sure, I'd have a question about your skewed_gaussian : your "beta" corresponds to wikipedia's "alpha" and you introduce a new parameter "gamma" that enters the definition of the asymmetry and may be subsequently fitted. Is that correct ?

Yes. We aimed for consistency and for reusing parameter names that had similar meanings.  But we also try to give the real definition in the docs.  But for the skewed Gaussian, our 'gamma' is the skewness -- 'alpha' in the wikipedia definition. 


As I was thinking about the Lorentzian and Pseudo-Voigt profiles, I was starting to mix up the "gamma" for the asymmetry and the one related to the HWHM of the Lorentzian profile. 

Right, that would be confusing.... Perhaps the generic asymmetry function should use 'alpha', and the skewed Gaussian definiton be changed (ater some deprecation)?


In order to be coherent with your site, it might be good to have skewed versions of the Built-in models (from a selfish point of view, I would particularly be interested in a skewed Lorentzian, pseudo-voigt and Pearson VII, as they are quite useful for diffraction data) as well as an "asymmetry model" (also based on the same form) for models built by the user. 

In fact, we already have StepModel, which use any of linear step, arctan, error function, or logistic function.  Multiplying such a model and any of the peak-like models will give an asymmetric peak, and give many choices for shape of the peak itself, and its asymmetry.  As a bonus, it can be used with the current version.


Concerning the functional to use to introduce the asymmetry, I am not aware of any particular preference for elastic scattering data. I have never come across anything but the definition a piecewise function with two hwhm's, which usually brings in more problems than solutions.

I'm not really an expert in X-ray diffraction, but I would be somewhat surprised if asymmetric peaks were needed very often, and would think that would be due to some instrumental issue.  But, it's fine to want to fit asymmetric peaks. 
 

Let me know how I can help. 

I think that trying out "StepModel() * PeakModel()", with a choice of "form='erf'" and so on for StepModel() and any of the PeakModels you list above.  If that can work for your data, a simple example would be much appreciated!
 

-- Pierre

--
You received this message because you are subscribed to the Google Groups "lmfit-py" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+unsubscribe@googlegroups.com.
To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.
To view this discussion on the web visit https://groups.google.com/d/msgid/lmfit-py/b35beac9-afd6-40c0-b13c-cc0f12560352%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
--Matt

peja...@gmail.com

unread,
Aug 25, 2017, 5:46:26 AM8/25/17
to lmfit-py
Hi Matt,

Right, that would be confusing.... Perhaps the generic asymmetry function should use 'alpha', and the skewed Gaussian definiton be changed (ater some deprecation)?

That sounds reasonable to me. 

I'm not really an expert in X-ray diffraction, but I would be somewhat surprised if asymmetric peaks were needed very often, and would think that would be due to some instrumental issue.  But, it's fine to want to fit asymmetric peaks. 

Actually, some wavelengths used in diffraction are intrinsically asymmetric, e.g. Cu K_{beta} due to the multiple allowed transitions that gives rise to it. Asymmetry is sometimes preferred over having to deal with the doublet Cu K_{alpha1}-Cu K_{alpha2} (if mirrors cannot be used to get rid of K_{alpha2}). You may call that an instrumental issue. 
Another frequent occurence is related to gradients, such as composition, or strain gradients in thin films. Anyway, sometimes one needs these asymmetric profiles ! 
 
 I think that trying out "StepModel() * PeakModel()", with a choice of "form='erf'" and so on for StepModel() and any of the PeakModels you list above.  If that can work for your data, a simple example would be much appreciated!

I have not tried yet with StepModel() * PeakModel() but what I have done is to use your asym() function this way :

from lmfit.models import LorentzianModel
from scipy.special import erf
from lmfit import Model

model = LorentzianModel(prefix='l_peak_')

def asym(x, center_asym=0, beta=1):
   return 1 + erf(beta*(x-center_asym))

def createAsymModel(x, model):
   return Model(asym) * model

asymModel = createAsymModel(x, model)
params = model.guess(y, x=x)
params += Model(asym).make_params(center_asym=x[np.argmax(y)])
out = asymModel.fit(y, params, x=x)
plt.plot(x, out.init_fit, 'k--', label='Intial values')
plt.plot(x, out.best_fit, 'y-', linewidth=2, label='Skewed Lor. fit')
print(out.fit_report(min_correl=0.25))

I needed to rename the 'center' parameter in asym() as I got the error message :

NameError:
Two models have parameters named "center". Use distinct names.

which is basically what I would like but don't know how to use...

I have however failed to make a function that would combine asym() and CreateAsymModel(). 
What I tried was :

model = LorentzianModel(prefix='l_peak_')

def AsymmetryModel(x, model, center_asym=0, beta=1):
    return Model(1 + erf(beta*(x-center_asym))) * model

asymModel = AsymmetryModel(x, model)
params = model.guess(y, x=x) 
params.add('center_asym', value=x[np.argmax(y)])

I get the following error message :

Traceback (most recent call last):

  File "<ipython-input-71-a3a6fce2b716>", line 1, in <module>
   runfile('/Users/janolin/Documents/DocumentsPro/Python/Codes/Test_asymetric.py', wdir='/Users/janolin/Documents/DocumentsPro/Python/Codes')

  File "//anaconda/lib/python3.5/site-packages/spyder/utils/site/sitecustomize.py", line 688, in runfile
   execfile(filename, namespace)

  File "//anaconda/lib/python3.5/site-packages/spyder/utils/site/sitecustomize.py", line 101, in execfile
   exec(compile(f.read(), filename, 'exec'), namespace)

  File "/.../Codes/Test_asymetric.py", line 186, in <module>
   asymModel = AsymmetryModel(x, model)

  File "/.../Codes/Test_asymetric.py", line 184, in AsymmetryModel
   return Model(1 + erf(beta*(x-center_asym))) * model

  File "//anaconda/lib/python3.5/site-packages/lmfit/model.py", line 111, in __init__
   self._parse_params()

  File "//anaconda/lib/python3.5/site-packages/lmfit/model.py", line 157, in _parse_params
   argspec = inspect.getargspec(self.func)

  File "//anaconda/lib/python3.5/inspect.py", line 1043, in getargspec
   getfullargspec(func)

  File "//anaconda/lib/python3.5/inspect.py", line 1095, in getfullargspec
   raise TypeError('unsupported callable') from ex

TypeError: unsupported callable

that seems to indicate that I can't call for model(). I have not been able to figure out why so far.

-- Pierre

peja...@gmail.com

unread,
Aug 25, 2017, 6:02:44 AM8/25/17
to lmfit-py
I forgot : here is the sample file I have been using for my tests. 2 columns : 2Theta and Intensity. In case you want some data to play. 
003cm.xy

peja...@gmail.com

unread,
Aug 25, 2017, 8:04:18 AM8/25/17
to lmfit-py
Found some time to play some more and I managed to get a function that returns an asymmetric version of the model passed to it.
I have made it so that the parameters would also be initialised, a bit like with a guess function.
Note : I have added a linear background in order to fit properly my data.
import numpy as np
import matplotlib.pyplot as plt

with open('003cm.xy') as f :
    data = np.loadtxt(f)
x = data[:,0]
y = data[:,1]

from lmfit.models import LinearModel
line_mod = LinearModel(prefix='bckd_')    # linear background
slopei = (y[np.argmax(x)]-y[np.argmin(x)])/(x.max()-x.min()) # intial slope
interi = (y[np.argmin(x)]-slopei*x.min()) # initial intercept
pars_lin = line_mod.make_params(intercept=interi, slope=slopei)

bkd_mod = line_mod                        # as I may chose between linear or
pars_bkd = pars_lin                       # constant (not shown here)

from lmfit.models import StepModel, ConstantModel
from lmfit.models import LorentzianModel, VoigtModel, GaussianModel
from lmfit import Parameter, Parameters

def AsymModel(model):
    """
    Creates an asymmetric version of the model and initialises the parameters
    """
    params = Parameters()
    params += model.guess(data=y, x=x)
    params += pars_bkd
    p1 = Parameter('Step_amplitude', 1.0)
    p2 = Parameter('Step_center', x[np.argmax(y)])
    p3 = Parameter('Step_sigma', 1.0)
    params.add_many(p1, p2, p3)
    return model * StepModel(form='erf', prefix='Step_'), params

asym_model, params = AsymModel(GaussianModel(prefix='g_peak_'))
tot_model = asym_model + bkd_mod
params += pars_bkd
out = tot_model.fit(y, params, x=x)
plt.plot(x, y, 'bo')
plt.plot(x, out.init_fit, 'k--', label='Intial values')
plt.plot(x, out.best_fit, 'y-', linewidth=2, label='Skewed Gauss. fit')
print(out.fit_report(min_correl=0.25))

It has been tested with LorentzianModel(), GaussianModel(), VoigtModel(). Seems to be working ok. 

I guess there should be some tests in the fucntion to make sure the "model" is not already skewed, but I don't know yet how to do that in python.
It should also be possible to find the center of the "model" and re-use that as the initialisation of the 'Step_center' parameter
There should also be a more concise, proper, complete (?) way to initialise the paramters for the Step function. That I don't know how to do. 
Maybe the functional used in the step function should be passed as a parameter to improve the versatility, maybe with 'erf' as default value ? 
I am sure there are still plenty of work to do to make it widely usable but this is my humble first step (been using python for a week...)

Comparing the results of the fit, I find that this function, when used with either GaussianModel() or VoigtModel() actually gives lower chi_square and Bayesian info than the SkewedGaussianModel() or skewed_voigt lineshape.

                         Asym(Gaussian)        SkewedGaussianModel    Asym(Voigt)       skewed_voigt
Chi_square 1 096 887 about 1 639 000 863 536 1 152 000
Bay.Info.Crit. 975 1007 951 971

The origin of this difference is beyond my current understanding.

-- Pierre

Matt Newville

unread,
Aug 25, 2017, 2:28:15 PM8/25/17
to lmfit-py
Hi Pierre,

It seems like you got this working pretty well, which is great!    This looks like a very useful example. Perhaps we should include it with the source kit and in the documentation?


And, yes, if you're using a lab anode X-ray source without a monochromator to separate the emission lines, you will see multiple peaks.

For the difference between you asymmetric peak, and the built-in skewed Gaussian and Voigt lineshapes/models:  maybe the center of the Peak and the center of the Step are different in your asymmetric peak case?  For the builtin skewed_gaussian and skewed_voigt, the centers are forced to be the same.




-- Pierre

--
You received this message because you are subscribed to the Google Groups "lmfit-py" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+unsubscribe@googlegroups.com.
To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.

For more options, visit https://groups.google.com/d/optout.


--Matt

Pierre-Eymeric Janolin

unread,
Aug 25, 2017, 3:43:54 PM8/25/17
to lmfi...@googlegroups.com

If you think this deserves to be included, I'd be delighted to have contributed.
About the centers this is a very valid point. Unfortunately I don't know how to make the two parameters equal.
About the line profile, you're absolutely right, but we don't have the chance to work at a synchrotron ;)
-- pierre


To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+u...@googlegroups.com.

To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.


--Matt

--
You received this message because you are subscribed to a topic in the Google Groups "lmfit-py" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/lmfit-py/aD51ufTwawU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to lmfit-py+u...@googlegroups.com.

To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.

Matt Newville

unread,
Aug 27, 2017, 9:16:50 PM8/27/17
to lmfit-py
 Pierre,


On Fri, Aug 25, 2017 at 2:43 PM, Pierre-Eymeric Janolin <peja...@gmail.com> wrote:

If you think this deserves to be included, I'd be delighted to have contributed.


Yes, please.  I think such an example would be very useful.
 

About the centers this is a very valid point. Unfortunately I don't know how to make the two parameters equal.


Following your example, you should be able to say:

    params['Step_center'].set(expr='g_peak_center')

or even
  
    p2 = Parameter('Step_center', expr='g_peak_center')

this will force 'Step_center' to have the same value as 'g_peak_center'.  The expression held in a Parameter's 'expr' can be nearly any valid python expression written in terms of the other parameter names and common Python / numpy functions and values.

Cheers,
--Matt

To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+unsubscribe@googlegroups.com.

To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.


--Matt

--
You received this message because you are subscribed to a topic in the Google Groups "lmfit-py" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/lmfit-py/aD51ufTwawU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to lmfit-py+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "lmfit-py" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+unsubscribe@googlegroups.com.

To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.

peja...@gmail.com

unread,
Sep 12, 2017, 11:59:19 AM9/12/17
to lmfit-py
Matt,

Sorry for the late answer, my university moved to a new campus and there are still a few glitches to fix here, like internet access...

Following your example, you should be able to say:

    params['Step_center'].set(expr='g_peak_center')

or even
  
    p2 = Parameter('Step_center', expr='g_peak_center')

this will force 'Step_center' to have the same value as 'g_peak_center'.  The expression held in a Parameter's 'expr' can be nearly any valid python expression written in terms of the other parameter names and common Python / numpy functions and values.

Thanks for showing me how to do that. It works like a charm. In order to make it more flexible, I was wondering whether it would be possible to call for the prefix that is defined when the function is called. 
That is, if instead of :

asym_model, params = AsymModel(GaussianModel(prefix='g_peak_')

I use a VoigtModel and therefore call this function with 
asym_model, params = AsymModel(VoigtModel(prefix='v_peak_')

Then I would have to change 
p2 = Parameter('Step_center', expr='g_peak_center')
into 
p2 = Parameter('Step_center', expr='v_peak_center')
within the function. 

I tried to understand how to get this value, but I could not make use of "VoigtModel.prefix" as it appears to be a "property" which I don't know how to handle.
A workaround would be to impose to call "AsymModel" with the prefix as a variable, to assign the prefix to the peak within the function and to use it to impose the two centers to be identical but this is not very elegant and seems a bit un-natural to me. 
Any hint ?

Thanks
Pierre

Matt Newville

unread,
Sep 12, 2017, 10:16:35 PM9/12/17
to lmfit-py
Hi Pierre,

On Tue, Sep 12, 2017 at 10:59 AM, <peja...@gmail.com> wrote:
Matt,

Sorry for the late answer, my university moved to a new campus and there are still a few glitches to fix here, like internet access...

Following your example, you should be able to say:

    params['Step_center'].set(expr='g_peak_center')

or even
  
    p2 = Parameter('Step_center', expr='g_peak_center')

this will force 'Step_center' to have the same value as 'g_peak_center'.  The expression held in a Parameter's 'expr' can be nearly any valid python expression written in terms of the other parameter names and common Python / numpy functions and values.

 
Thanks for showing me how to do that. It works like a charm. In order to make it more flexible, I was wondering whether it would be possible to call for the prefix that is defined when the function is called. 
That is, if instead of :

asym_model, params = AsymModel(GaussianModel(prefix='g_peak_')

I use a VoigtModel and therefore call this function with 
asym_model, params = AsymModel(VoigtModel(prefix='v_peak_')

Then I would have to change 
p2 = Parameter('Step_center', expr='g_peak_center')
into 
p2 = Parameter('Step_center', expr='v_peak_center')
within the function. 


I think you might be able to use the "param_hints" to facilitate this, sort of like the magic that happens for the "fwhm" and "height" parameters.

But maybe that's too clunky, and we should think about the design a bit more. 


I tried to understand how to get this value, but I could not make use of "VoigtModel.prefix" as it appears to be a "property" which I don't know how to handle.
A workaround would be to impose to call "AsymModel" with the prefix as a variable, to assign the prefix to the peak within the function and to use it to impose the two centers to be identical but this is not very elegant and seems a bit un-natural to me. 
Any hint ?

Maybe we could take 'StepModel' as a guide and have something like

   AsymPeak(form='voigt', prefix='v_peak_')

where form was one the typical selection of peak-like functions.   I think that would be easy enough to implement, and give a decent set of choice of peaks to make asymmetric.

Does that seem usable enough?  Any other suggestions for what would (and would not) work?

--Matt

peja...@gmail.com

unread,
Sep 13, 2017, 9:42:42 AM9/13/17
to lmfit-py
Hi Matt,

I tried to create a function passing a form and a prefix in such a way : 

def AsymPeak(form, form_prefix):
    """
    Creates an asymmetric version of the model and initialises the parameters
    """
    model_name = form+'Model'
        p11 = Parameter('Step_amplitude', 1.0)
    p21 = Parameter('Step_center', expr=form_prefix+'_center')
    p31 = Parameter('Step_sigma', 1.0)   
        params1 = Parameters()
    params1 += model_name.guess(data=y, x=x)
    params1 += pars_bkd
    params1.add_many(p11, p21, p31)
    return model_name(prefix=form_prefix) * StepModel(form='erf', prefix='Step_'), params1 

I don't get any error when I set the center of the Step to be equal to the center of the peak, which is nice.
However model_name being a string I cannot use model_name.guess; I get an "AttributeError: 'str' object has no attribute 'guess'" 
Having the model recognised would enable both to be able to both use the .guess function as well as to implement a test as to whether the form passed to the AsymPeak function is indeed one of the recognise model and therefore allow for easier error handling, at least IMHO. 
Would you have any advice ?

Thanks
-Pierre

Matt Newville

unread,
Sep 13, 2017, 6:12:29 PM9/13/17
to lmfit-py
I think we probably need an AsymPeakModel class, derived from Model.  It might be sort of similar to StepModel in that it takes a 'form'-like argument for which peak to use, and it should set parameter hints, not create a Parameters object. 

I'm afraid I don't have a lot of time to devote to it this week or next, but maybe in a few weeks...


Thanks
-Pierre

--
You received this message because you are subscribed to the Google Groups "lmfit-py" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+unsubscribe@googlegroups.com.
To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.

For more options, visit https://groups.google.com/d/optout.



--Matt

Zachary Mathe

unread,
Sep 5, 2018, 10:01:39 AM9/5/18
to lmfit-py
Hello Matt,

Far up in this thread, you wrote:
In fact, there is a skewed Voigt function in lineshapes.py (skewed_voigt(), see https://github.com/lmfit/lmfit-py/blob/master/lmfit/lineshapes.py#L230), though it's not exposed as a builtin Model -- that could be fixed easily. 

 Could this be done? I would like to use a skewed Voigt model, and for the sake of easy collaboration with my coworkers (we are all new to python), it would be nice to have the builtin available.

(I apologize if this is not the correct place to communicate this)

Thanks,
Zac

Thanks
-Pierre
To unsubscribe from this group and stop receiving emails from it, send an email to lmfit-py+u...@googlegroups.com.

To post to this group, send email to lmfi...@googlegroups.com.
Visit this group at https://groups.google.com/group/lmfit-py.



--Matt

Matt Newville

unread,
Sep 5, 2018, 10:44:55 AM9/5/18
to lmfit-py
Hi Zac,

On Wed, Sep 5, 2018 at 9:01 AM Zachary Mathe <zacm...@gmail.com> wrote:
Hello Matt,

Far up in this thread, you wrote:
In fact, there is a skewed Voigt function in lineshapes.py (skewed_voigt(), see https://github.com/lmfit/lmfit-py/blob/master/lmfit/lineshapes.py#L230), though it's not exposed as a builtin Model -- that could be fixed easily. 

 Could this be done? I would like to use a skewed Voigt model, and for the sake of easy collaboration with my coworkers (we are all new to python), it would be nice to have the builtin available.


Yes, that's true that there is a SkewedGaussianModel, but not a SkewedVoigtModel. You can just get most of the way there with

    from lmfit import Model
    from lmfit.lineshapes import skewed_voigt 
    sv_model = Model(skewed_voigt)

Note that  `help(skewed_voigt)` will give you the functional form and the parameter names: the Voigt parameters plus `skew`, which sets the width of the error function multiplying the Voigt function.

A SkewedVoigtModel would be a fine thing to add to the built-in models,  and not that hard to do. 
But would really only add a few features to the above:
    1) set a hint that sigma > 0
    2) give expressions for fwhm and height parameters
    3) provide a not-that-perferct `guess()` method.

still those are nice features to have.  We'll put this on the ToDo list, but I hope you can get started with the above code....

--Matt 

Zachary Mathe

unread,
Sep 5, 2018, 11:16:17 AM9/5/18
to lmfit-py
Hi Matt,

Thank you for the fast reply! Sadly I cannot get the ad-hoc version to work: the gamma parameter seems to be missing from the parameters dictionary.

Here is what i've done:

> mod = Model(skewed_voigt, prefix='t_')
> modpars = mod.make_params()
> modpars
Parameters([('t_skew', <Parameter 't_skew', 0.0, bounds=[-inf:inf]>),
                     ('t_sigma', <Parameter 't_sigma', 1.0, bounds=[-inf:inf]>),
                     ('t_center', <Parameter 't_center', 0.0, bounds=[-inf:inf]>),
                     ('t_amplitude', <Parameter 't_amplitude', 1.0, bounds=[-inf:inf]>)])

So, when I attempt to set an initial value and min/max for gamma, I get a KeyError.

Matt Newville

unread,
Sep 5, 2018, 12:26:26 PM9/5/18
to lmfit-py
Hi Zac,

On Wed, Sep 5, 2018 at 10:16 AM Zachary Mathe <zacm...@gmail.com> wrote:
Hi Matt,

Thank you for the fast reply! Sadly I cannot get the ad-hoc version to work: the gamma parameter seems to be missing from the parameters dictionary.

Here is what i've done:

> mod = Model(skewed_voigt, prefix='t_')
> modpars = mod.make_params()
> modpars
Parameters([('t_skew', <Parameter 't_skew', 0.0, bounds=[-inf:inf]>),
                     ('t_sigma', <Parameter 't_sigma', 1.0, bounds=[-inf:inf]>),
                     ('t_center', <Parameter 't_center', 0.0, bounds=[-inf:inf]>),
                     ('t_amplitude', <Parameter 't_amplitude', 1.0, bounds=[-inf:inf]>)])

So, when I attempt to set an initial value and min/max for gamma, I get a KeyError.


Oh, sorry.  With the voigt lineshape function,  `gamma` defaults to `None`.  If that is used, it will default to being equal to `sigma`. But it also means that a parameter is not generated for the model by default.  You'll have to explicitly set a hint for gamma to do that:

     mod = Model(skewed_voigt, prefix='t_')
     mod.set_param_hint('gamma', value=1.0)
     modpars = mod.make_params()

Then modpars will have a variable parameter for `t_gamma`.

--Matt

Reply all
Reply to author
Forward
0 new messages