diff options
Diffstat (limited to 'src/silx/math/fit/functions.pyx')
-rw-r--r-- | src/silx/math/fit/functions.pyx | 162 |
1 files changed, 97 insertions, 65 deletions
diff --git a/src/silx/math/fit/functions.pyx b/src/silx/math/fit/functions.pyx index a69086c..e7102a5 100644 --- a/src/silx/math/fit/functions.pyx +++ b/src/silx/math/fit/functions.pyx @@ -33,6 +33,7 @@ List of fit functions: - :func:`sum_apvoigt` - :func:`sum_pvoigt` - :func:`sum_splitpvoigt` + - :func:`sum_splitpvoigt2` - :func:`sum_lorentz` - :func:`sum_alorentz` @@ -143,9 +144,7 @@ def sum_gauss(x, *params): double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No gaussian parameters specified. " + - "At least 3 parameters are required.") + _validate_parameters(params, 3) # ensure float64 (double) type and 1D contiguous data layout in memory x_c = numpy.array(x, @@ -191,9 +190,7 @@ def sum_agauss(x, *params): double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No gaussian parameters specified. " + - "At least 3 parameters are required.") + _validate_parameters(params, 3) x_c = numpy.array(x, copy=False, @@ -241,9 +238,7 @@ def sum_fastagauss(x, *params): double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No gaussian parameters specified. " + - "At least 3 parameters are required.") + _validate_parameters(params, 3) x_c = numpy.array(x, copy=False, @@ -290,9 +285,7 @@ def sum_splitgauss(x, *params): double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No gaussian parameters specified. " + - "At least 4 parameters are required.") + _validate_parameters(params, 4) x_c = numpy.array(x, copy=False, @@ -327,7 +320,7 @@ def sum_apvoigt(x, *params): - *area* is the area underneath both G(x) and L(x) - *centroid* is the peak x-coordinate for both functions - *fwhm* is the full-width at half maximum of both functions - - *eta* is the Lorentz factor: PV(x) = eta * L(x) + (1 - eta) * G(x) + - *eta* is the Lorentzian fraction: PV(x) = eta * L(x) + (1 - eta) * G(x) :param x: Independent variable where the gaussians are calculated :type x: numpy.ndarray @@ -341,9 +334,8 @@ def sum_apvoigt(x, *params): double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No parameters specified. " + - "At least 4 parameters are required.") + _validate_parameters(params, 4) + x_c = numpy.array(x, copy=False, dtype=numpy.float64, @@ -377,7 +369,7 @@ def sum_pvoigt(x, *params): - *height* is the peak amplitude of G(x) and L(x) - *centroid* is the peak x-coordinate for both functions - *fwhm* is the full-width at half maximum of both functions - - *eta* is the Lorentz factor: PV(x) = eta * L(x) + (1 - eta) * G(x) + - *eta* is the Lorentzian fraction: PV(x) = eta * L(x) + (1 - eta) * G(x) :param x: Independent variable where the gaussians are calculated :type x: numpy.ndarray @@ -391,9 +383,7 @@ def sum_pvoigt(x, *params): double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No parameters specified. " + - "At least 4 parameters are required.") + _validate_parameters(params, 4) x_c = numpy.array(x, copy=False, @@ -425,13 +415,13 @@ def sum_splitpvoigt(x, *params): profile using a linear combination of a Gaussian curve ``G(x)`` and a Lorentzian curve ``L(x)`` instead of their convolution. - - *height* is the peak amplitudefor G(x) and L(x) + - *height* is the peak amplitude for G(x) and L(x) - *centroid* is the peak x-coordinate for both functions - *fwhm1* is the full-width at half maximum of both functions when ``x < centroid`` - *fwhm2* is the full-width at half maximum of both functions when ``x > centroid`` - - *eta* is the Lorentz factor: PV(x) = eta * L(x) + (1 - eta) * G(x) + - *eta* is the Lorentzian fraction: PV(x) = eta * L(x) + (1 - eta) * G(x) :param x: Independent variable where the gaussians are calculated :type x: numpy.ndarray @@ -446,9 +436,7 @@ def sum_splitpvoigt(x, *params): double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No parameters specified. " + - "At least 5 parameters are required.") + _validate_parameters(params, 5) x_c = numpy.array(x, copy=False, @@ -472,6 +460,60 @@ def sum_splitpvoigt(x, *params): return numpy.asarray(y_c).reshape(x.shape) +def sum_splitpvoigt2(x, *params): + """Return a sum of split pseudo-Voigt functions, defined by *(height, + centroid, fwhm1, fwhm2, eta1, eta2)*. + + The pseudo-Voigt profile ``PV(x)`` is an approximation of the Voigt + profile using a linear combination of a Gaussian curve ``G(x)`` and a + Lorentzian curve ``L(x)`` instead of their convolution. + + - *height* is the peak amplitude for G(x) and L(x) + - *centroid* is the peak x-coordinate for both functions + - *fwhm1* is the full-width at half maximum of both functions + when ``x < centroid`` + - *fwhm2* is the full-width at half maximum of both functions + when ``x > centroid`` + - *eta1* is the Lorentzian fraction when ``x < centroid`` + - *eta2* is the Lorentzian fraction when ``x > centroid`` + + :param x: Independent variable where the gaussians are calculated + :type x: numpy.ndarray + :param params: Array of pseudo-Voigt parameters (length must be a multiple + of 6): + *(height1, centroid1, fwhm11, fwhm21, eta11, eta21,...)* + :return: Array of sum of split pseudo-Voigt functions at each ``x`` + coordinate + """ + cdef: + double[::1] x_c + double[::1] params_c + double[::1] y_c + + _validate_parameters(params, 6) + + x_c = numpy.array(x, + copy=False, + dtype=numpy.float64, + order='C').reshape(-1) + params_c = numpy.array(params, + copy=False, + dtype=numpy.float64, + order='C').reshape(-1) + y_c = numpy.empty(shape=(x.size,), + dtype=numpy.float64) + + status = functions_wrapper.sum_splitpvoigt2( + &x_c[0], x.size, + ¶ms_c[0], params_c.size, + &y_c[0]) + + if status: + raise IndexError("Wrong number of parameters for function") + + return numpy.asarray(y_c).reshape(x.shape) + + def sum_lorentz(x, *params): """Return a sum of Lorentz distributions, also known as Cauchy distribution, defined by *(height, centroid, fwhm)*. @@ -493,9 +535,7 @@ def sum_lorentz(x, *params): double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No parameters specified. " + - "At least 3 parameters are required.") + _validate_parameters(params, 3) x_c = numpy.array(x, copy=False, @@ -540,9 +580,7 @@ def sum_alorentz(x, *params): double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No parameters specified. " + - "At least 3 parameters are required.") + _validate_parameters(params, 3) x_c = numpy.array(x, copy=False, @@ -588,9 +626,7 @@ def sum_splitlorentz(x, *params): double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No parameters specified. " + - "At least 4 parameters are required.") + _validate_parameters(params, 4) x_c = numpy.array(x, copy=False, @@ -636,9 +672,8 @@ def sum_stepdown(x, *params): double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No parameters specified. " + - "At least 3 parameters are required.") + _validate_parameters(params, 3) + x_c = numpy.array(x, copy=False, dtype=numpy.float64, @@ -684,9 +719,7 @@ def sum_stepup(x, *params): double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No parameters specified. " + - "At least 3 parameters are required.") + _validate_parameters(params, 3) x_c = numpy.array(x, copy=False, @@ -735,9 +768,7 @@ def sum_slit(x, *params): double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No parameters specified. " + - "At least 4 parameters are required.") + _validate_parameters(params, 4) x_c = numpy.array(x, copy=False, @@ -797,9 +828,9 @@ def sum_ahypermet(x, *params, *(area1, position1, fwhm1, st_area_r1, st_slope_r1, lt_area_r1, lt_slope_r1, step_height_r1...)* :param gaussian_term: If ``True``, enable gaussian term. Default ``True`` - :param st_term: If ``True``, enable gaussian term. Default ``True`` - :param lt_term: If ``True``, enable gaussian term. Default ``True`` - :param step_term: If ``True``, enable gaussian term. Default ``True`` + :param st_term: If ``True``, enable short tail term. Default ``True`` + :param lt_term: If ``True``, enable long tail term. Default ``True`` + :param step_term: If ``True``, enable step term. Default ``True`` :return: Array of sum of hypermet functions at each ``x`` coordinate """ cdef: @@ -807,9 +838,7 @@ def sum_ahypermet(x, *params, double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No parameters specified. " + - "At least 8 parameters are required.") + _validate_parameters(params, 8) # Sum binary flags to activate various terms of the equation tail_flags = 1 if gaussian_term else 0 @@ -883,9 +912,9 @@ def sum_fastahypermet(x, *params, *(area1, position1, fwhm1, st_area_r1, st_slope_r1, lt_area_r1, lt_slope_r1, step_height_r1...)* :param gaussian_term: If ``True``, enable gaussian term. Default ``True`` - :param st_term: If ``True``, enable gaussian term. Default ``True`` - :param lt_term: If ``True``, enable gaussian term. Default ``True`` - :param step_term: If ``True``, enable gaussian term. Default ``True`` + :param st_term: If ``True``, enable short tail term. Default ``True`` + :param lt_term: If ``True``, enable long tail term. Default ``True`` + :param step_term: If ``True``, enable step term. Default ``True`` :return: Array of sum of hypermet functions at each ``x`` coordinate """ cdef: @@ -893,9 +922,7 @@ def sum_fastahypermet(x, *params, double[::1] params_c double[::1] y_c - if not len(params): - raise IndexError("No parameters specified. " + - "At least 8 parameters are required.") + _validate_parameters(params, 8) # Sum binary flags to activate various terms of the equation tail_flags = 1 if gaussian_term else 0 @@ -955,7 +982,7 @@ def atan_stepup(x, a, b, c): return a * (0.5 + (numpy.arctan((1.0 * x - b) / c) / numpy.pi)) -def periodic_gauss(x, *pars): +def periodic_gauss(x, *params): """ Return a sum of gaussian functions defined by *(npeaks, delta, height, centroid, fwhm)*, @@ -968,17 +995,22 @@ def periodic_gauss(x, *pars): - *fwhm* is the full-width at half maximum for all the gaussians :param x: Independent variable where the function is calculated - :param pars: *(npeaks, delta, height, centroid, fwhm)* + :param params: *(npeaks, delta, height, centroid, fwhm)* :return: Sum of ``npeaks`` gaussians """ - if not len(pars): - raise IndexError("No parameters specified. " + - "At least 5 parameters are required.") + _validate_parameters(params, 5) - newpars = numpy.zeros((pars[0], 3), numpy.float64) - for i in range(int(pars[0])): - newpars[i, 0] = pars[2] - newpars[i, 1] = pars[3] + i * pars[1] - newpars[:, 2] = pars[4] + newpars = numpy.zeros((params[0], 3), numpy.float64) + for i in range(int(params[0])): + newpars[i, 0] = params[2] + newpars[i, 1] = params[3] + i * params[1] + newpars[:, 2] = params[4] return sum_gauss(x, newpars) + + +def _validate_parameters(params, multiple): + if len(params) == 0: + raise IndexError("No parameters specified.") + if len(params) % multiple: + raise IndexError(f"The number of parameters should be a multiple of {multiple}.") |