Skip to content

Commit d295689

Browse files
adding peak_voigt model
1 parent 9caae97 commit d295689

1 file changed

Lines changed: 149 additions & 0 deletions

File tree

sasmodels/models/peak_voigt.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
r"""
2+
This model describes a pseudo-Voigt shaped peak on a flat background.
3+
4+
Definition
5+
----------
6+
7+
This pseudo-Voigt peak function is a weighted linear summation of
8+
Lorentzian (L) and Gaussian (G) peak shapes.
9+
It is a popular function for modelling peak shape.
10+
It can be tailored to any specific peak shape and it can also produce a peak shape with asymmetry.
11+
12+
The scattering intensity $I(q)$ is calculated as
13+
14+
.. math::
15+
16+
I(q) = scale \cdot \left[ w_f \cdot I(q)_L + (1 - w_f) \cdot I(q)_G \right] + background
17+
18+
where $w_f$ is a weighting factor and
19+
20+
.. math::
21+
22+
I(q)_L = \frac{1}{1 + \left( \frac{q - q_0}{HWHM} \right)^2}
23+
24+
I(q)_G = \exp\left[ -\frac{1}{2} (q - q_0)^2 / \sigma^2 \right]
25+
26+
The peak is taken to be centered at $q_0$ with a HWHM (half-width
27+
half-maximum) of $1.17741\,\sigma$, where $\sigma$ is the standard deviation
28+
of the Gaussian. In other words, the widths of the Lorentzian and the
29+
Gaussian have been coupled for convenience of parameterisation:
30+
31+
.. math::
32+
33+
\sigma = HWHM / \sqrt{2 \ln 2} = HWHM / 1.17741
34+
35+
When $w_f = 1$ a Lorentzian peak is returned, and when $w_f = 0$ a
36+
Gaussian peak is returned.
37+
38+
For 2D data the scattering intensity is calculated in the same way as 1D,
39+
where the $q$ vector is defined as
40+
41+
.. math::
42+
43+
q = \sqrt{q_x^2 + q_y^2}
44+
45+
46+
Validation
47+
----------
48+
49+
The pseudo-Voigt peak reduces exactly to a pure Lorentzian for $w_f = 1$
50+
and to a pure Gaussian for $w_f = 0$; both limits were checked against their
51+
analytic values (see tests section at the end).
52+
The full pseudo-Voigt shape has also been compared, for identical
53+
parameters, against a slightly different SasView implementation (https://marketplace.sasview.org/models/127/)
54+
of the same function and gives the same result.
55+
56+
57+
References
58+
----------
59+
60+
1. L A Feigin, D I Svergun, G W Taylor
61+
Structure Analysis by Small-Angle X-ray and Neutron Scattering
62+
Springer (1987)
63+
64+
2. Aaron L. Stancik, Eric B. Brauns
65+
A simple asymmetric lineshape for fitting infrared absorption spectra
66+
Vibrational Spectroscopy 47 (2008) 66-69
67+
68+
69+
Authorship and Verification
70+
----------------------------
71+
72+
* **Author:** Steve King **Date:** 24 June 2020
73+
74+
* **Authors:** Marianne Imperor-Clerc (marianne.imperor@cnrs.fr)
75+
Anirban Mandal (mandalanirban2023@gmail.com)
76+
77+
* **Last Modified by:** Anirban Mandal **Date:** 06 July 2026
78+
79+
* **Last Reviewed by:** Steve King **Date:**
80+
81+
"""
82+
83+
import numpy as np
84+
from numpy import inf, errstate
85+
86+
name = "peak_voigt"
87+
title = "Single pseudo-Voigt peak"
88+
description = """\
89+
I(q) = scale*peak + background
90+
"""
91+
92+
category = "shape-independent"
93+
94+
parameters = [["w_f", "", 0.8, [0, 1], "", "lorentzian/gaussian weighting factor"],
95+
["peak_pos", "1/Ang", 0.05, [0, inf], "", "Position of the peak"],
96+
["peak_hwhm", "1/Ang", 0.01, [0, 1], "", "HWHM of the peak"]]
97+
98+
99+
def Ipeak(q, wf, q0, hwhm):
100+
"""
101+
When $w_f$ = 1 a Lorentzian peak is returned, and when $w_f$ = 0 a
102+
Gaussian peak is returned.
103+
104+
The peak is taken to be centered at $q_0$ with a HWHM (half-width
105+
half-maximum) for the Lorentzian and sigma = HWHM / 1.17741 for the
106+
Gaussian, where sigma is the standard deviation of the Gaussian. In
107+
other words, the widths of the Lorentzian and the Gaussian have been
108+
coupled for convenience of parameterisation.
109+
"""
110+
cste = np.sqrt(2 * np.log(2))
111+
# cste = 1.17741
112+
sigma = hwhm / cste
113+
intensity = (wf * (1 / (1 + ((q - q0)**2.0 / hwhm**2.0)))) + \
114+
((1.0 - wf) * np.exp((-0.5 * (q - q0)**2.0) / (sigma**2.0)))
115+
return intensity
116+
117+
118+
def Iq(q, w_f, peak_pos, peak_hwhm):
119+
"""
120+
w_f: weighting coefficient in the pseudo-Voigt peak function;
121+
w_f = 1 for a Lorentzian and w_f = 0 for a Gaussian peak.
122+
peak_pos: position of the peak
123+
peak_hwhm: HWHM of the peak
124+
"""
125+
126+
with errstate(divide='ignore'):
127+
L = Ipeak(q, w_f, peak_pos, peak_hwhm)
128+
129+
return L
130+
131+
Iq.vectorized = True # Iq accepts an array of q values
132+
133+
tests = [
134+
# pure Lorentzian (w_f = 1): peak centre, half-width, and 2 x HWHM
135+
[{"scale": 1.0, "background": 0.0, "w_f": 1.0,
136+
"peak_pos": 0.05, "peak_hwhm": 0.01}, 0.05, 1.0],
137+
[{"scale": 1.0, "background": 0.0, "w_f": 1.0,
138+
"peak_pos": 0.05, "peak_hwhm": 0.01}, 0.06, 0.5],
139+
[{"scale": 1.0, "background": 0.0, "w_f": 1.0,
140+
"peak_pos": 0.05, "peak_hwhm": 0.01}, 0.07, 0.2],
141+
# pure Gaussian (w_f = 0): half-width is 0.5 by definition, 2 x HWHM = 1/16
142+
[{"scale": 1.0, "background": 0.0, "w_f": 0.0,
143+
"peak_pos": 0.05, "peak_hwhm": 0.01}, 0.06, 0.5],
144+
[{"scale": 1.0, "background": 0.0, "w_f": 0.0,
145+
"peak_pos": 0.05, "peak_hwhm": 0.01}, 0.07, 0.0625],
146+
# mixed pseudo-Voigt (w_f = 0.8) away from the centre
147+
[{"scale": 1.0, "background": 0.0, "w_f": 0.8,
148+
"peak_pos": 0.05, "peak_hwhm": 0.01}, 0.07, 0.1725],
149+
]

0 commit comments

Comments
 (0)