#/*########################################################################## # Copyright (c) 2004-2016 European Synchrotron Radiation Facility # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # #############################################################################*/ /* This file provides fit functions. It is adapted from PyMca source file "SpecFitFuns.c". The main difference with the original code is that this code does not handle the python wrapping, which is done elsewhere using cython. Authors: V.A. Sole, P. Knobel License: MIT Last modified: 17/06/2016 */ #include #include #include #include "functions.h" #ifndef M_PI #define M_PI 3.1415926535 #endif #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #if defined(_WIN32) #define erf myerf #define erfc myerfc #endif #define LOG2 0.69314718055994529 int test_params(int len_params, int len_params_one_function, char* fun_name, char* param_names) { if (len_params % len_params_one_function) { printf("[%s]Error: Number of parameters must be a multiple of %d.", fun_name, len_params_one_function); printf("\nParameters expected for %s: %s\n", fun_name, param_names); return(1); } if (len_params == 0) { printf("[%s]Error: No parameters specified.", fun_name); printf("\nParameters expected for %s: %s\n", fun_name, param_names); return(1); } return(0); } /* Complementary error function for a single value*/ double myerfc(double x) { double z; double t; double r; z=fabs(x); t=1.0/(1.0+0.5*z); r=t * exp(-z * z - 1.26551223 + t * (1.00002368 + t * (0.3740916 + t * (0.09678418 + t * (-0.18628806 + t * (0.27886807 + t * (-1.13520398 + t * (1.48851587 + t * (-0.82215223+t*0.17087277))))))))); if (x<0) r=2.0-r; return (r); } /* Gauss error function for a single value*/ double myerf(double x) { return (1.0 - myerfc(x)); } /* Gauss error function for an array y[i]=erf(x[i]) returns status code 0 */ int erf_array(double* x, int len_x, double* y) { int j; for (j=0; j centroid) Parameters: ----------- - x: Independant variable where the gaussians are calculated. - len_x: Number of elements in the x array. - pgauss: Array of gaussian parameters: (height1, centroid1, fwhm11, fwhm21, height2, centroid2, fwhm12, fwhm22,...) - len_pgauss: Number of elements in the pgauss array. Must be a multiple of 4. - y: Output array. Must have memory allocated for the same number of elements as x (len_x). Adapted from PyMca module SpecFitFuns */ int sum_splitgauss(double* x, int len_x, double* pgauss, int len_pgauss, double* y) { int i, j; double dhelp, inv_two_sqrt_two_log2, sigma1, sigma2; double fwhm1, fwhm2, centroid, height; if (test_params(len_pgauss, 4, "sum_splitgauss", "height, centroid, fwhm1, fwhm2")) { return(1); } /* Initialize output array */ for (j=0; j 0) { /* Use fwhm2 when x > centroid */ dhelp = dhelp / sigma2; } else { /* Use fwhm1 when x < centroid */ dhelp = dhelp / sigma1; } if (dhelp <= 20) { y[j] += height * exp (-0.5 * dhelp * dhelp); } } } return(0); } /* sum_apvoigt Sum of pseudo-Voigt functions, defined by (area, centroid, fwhm, eta). 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. *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) Parameters: ----------- - x: Independant variable where the gaussians are calculated. - len_x: Number of elements in the x array. - pvoigt: Array of Voigt function parameters: (area1, centroid1, fwhm1, eta1, area2, centroid2, fwhm2, eta2,...) - len_voigt: Number of elements in the pvoigt array. Must be a multiple of 4. - y: Output array. Must have memory allocated for the same number of elements as x (len_x). Adapted from PyMca module SpecFitFuns */ int sum_apvoigt(double* x, int len_x, double* pvoigt, int len_pvoigt, double* y) { int i, j; double dhelp, inv_two_sqrt_two_log2, sqrt2PI, sigma, height; double area, centroid, fwhm, eta; if (test_params(len_pvoigt, 4, "sum_apvoigt", "area, centroid, fwhm, eta")) { return(1); } /* Initialize output array */ for (j=0; j centroid *eta* is the Lorentz factor: PV(x) = eta * L(x) + (1 - eta) * G(x) Parameters: ----------- - x: Independant variable where the gaussians are calculated. - len_x: Number of elements in the x array. - pvoigt: Array of Voigt function parameters: (height1, centroid1, fwhm11, fwhm21, eta1, ...) - len_voigt: Number of elements in the pvoigt array. Must be a multiple of 5. - y: Output array. Must have memory allocated for the same number of elements as x (len_x). Adapted from PyMca module SpecFitFuns */ int sum_splitpvoigt(double* x, int len_x, double* pvoigt, int len_pvoigt, double* y) { int i, j; double dhelp, inv_two_sqrt_two_log2, x_minus_centroid, sigma1, sigma2; double height, centroid, fwhm1, fwhm2, eta; if (test_params(len_pvoigt, 5, "sum_splitpvoigt", "height, centroid, fwhm1, fwhm2, eta")) { return(1); } /* Initialize output array */ for (j=0; j centroid */ if (x_minus_centroid > 0) { /* Lorentzian term */ dhelp = x_minus_centroid / (0.5 * fwhm2); dhelp = 1.0 + (dhelp * dhelp); y[j] += eta * height / dhelp; /* Gaussian term */ dhelp = x_minus_centroid / sigma2; if (dhelp <= 35) { y[j] += (1.0 - eta) * height * exp (-0.5 * dhelp * dhelp); } } /* Use fwhm1 when x < centroid */ else { /* Lorentzian term */ dhelp = x_minus_centroid / (0.5 * fwhm1); dhelp = 1.0 + (dhelp * dhelp); y[j] += eta * height / dhelp; /* Gaussian term */ dhelp = x_minus_centroid / sigma1; if (dhelp <= 35) { y[j] += (1.0 - eta) * height * exp (-0.5 * dhelp * dhelp); } } } } return(0); } /* sum_lorentz Sum of Lorentz functions, defined by (height, centroid, fwhm). *height* is the peak amplitude *centroid* is the peak's x-coordinate *fwhm* is the full-width at half maximum Parameters: ----------- - x: Independant variable where the Lorentzians are calculated. - len_x: Number of elements in the x array. - plorentz: Array of lorentz function parameters: (height1, centroid1, fwhm1, ...) - len_lorentz: Number of elements in the plorentz array. Must be a multiple of 3. - y: Output array. Must have memory allocated for the same number of elements as x (len_x). Adapted from PyMca module SpecFitFuns */ int sum_lorentz(double* x, int len_x, double* plorentz, int len_plorentz, double* y) { int i, j; double dhelp; double height, centroid, fwhm; if (test_params(len_plorentz, 3, "sum_lorentz", "height, centroid, fwhm")) { return(1); } /* Initialize output array */ for (j=0; j centroid Parameters: ----------- - x: Independant variable where the Lorentzians are calculated. - len_x: Number of elements in the x array. - plorentz: Array of lorentz function parameters: (height1, centroid1, fwhm11, fwhm21 ...) - len_lorentz: Number of elements in the plorentz array. Must be a multiple of 4. - y: Output array. Must have memory allocated for the same number of elements as x (len_x). Adapted from PyMca module SpecFitFuns */ int sum_splitlorentz(double* x, int len_x, double* plorentz, int len_plorentz, double* y) { int i, j; double dhelp; double height, centroid, fwhm1, fwhm2; if (test_params(len_plorentz, 4, "sum_splitlorentz", "height, centroid, fwhm1, fwhm2")) { return(1); } /* Initialize output array */ for (j=0; j0) { dhelp = dhelp / (0.5 * fwhm2); } else { dhelp = dhelp / (0.5 * fwhm1); } dhelp = 1.0 + (dhelp * dhelp); y[j] += height / dhelp; } } return(0); } /* sum_stepdown Sum of stepdown functions, defined by (height, centroid, fwhm). *height* is the step amplitude *centroid* is the step's x-coordinate *fwhm* is the full-width at half maximum of the derivative Parameters: ----------- - x: Independant variable where the stepdown functions are calculated. - len_x: Number of elements in the x array. - pdstep: Array of downstpe function parameters: (height1, centroid1, fwhm1, ...) - len_pdstep: Number of elements in the pdstep array. Must be a multiple of 3. - y: Output array. Must have memory allocated for the same number of elements as x (len_x). Adapted from PyMca module SpecFitFuns */ int sum_stepdown(double* x, int len_x, double* pdstep, int len_pdstep, double* y) { int i, j; double dhelp, sqrt2_inv_2_sqrt_two_log2 ; double height, centroid, fwhm; if (test_params(len_pdstep, 3, "sum_stepdown", "height, centroid, fwhm")) { return(1); } /* Initialize output array */ for (j=0; j>1) & 1; lt_term_flag = (tail_flags>>2) & 1; step_term_flag = (tail_flags>>3) & 1; /* Initialize output array */ for (j=0; j epsilon) { c1 = st_area_r * 0.5 * \ erfc((x_minus_position/sigma_sqrt2) + 0.5 * sigma_sqrt2 / st_slope_r); y[j] += ((area * c1) / st_slope_r) * \ exp(0.5 * (sigma / st_slope_r) * (sigma / st_slope_r) + \ (x_minus_position / st_slope_r)); } } /* lt term */ if (lt_term_flag) { if (fabs(lt_slope_r) > epsilon) { c1 = lt_area_r * \ 0.5 * erfc((x_minus_position/sigma_sqrt2) + 0.5 * sigma_sqrt2 / lt_slope_r); y[j] += ((area * c1) / lt_slope_r) * \ exp(0.5 * (sigma / lt_slope_r) * (sigma / lt_slope_r) + \ (x_minus_position / lt_slope_r)); } } /* step term flag */ if (step_term_flag) { y[j] += step_height_r * (area / (sigma * sqrt2PI)) * \ 0.5 * erfc(x_minus_position / sigma_sqrt2); } } } return(0); } /* sum_fastahypermet Sum of hypermet functions, defined by (area, position, fwhm, st_area_r, st_slope_r, lt_area_r, lt_slope_r, step_height_r). - *area* is the area underneath the gaussian peak - *position* is the center of the various peaks and the position of the step down - *fwhm* is the full-width at half maximum of the terms - *st_area_r* is factor between the gaussian area and the area of the short tail term - *st_slope_r* is a parameter related to the slope of the short tail in the low ``x`` values (the lower, the steeper) - *lt_area_r* is factor between the gaussian area and the area of the long tail term - *lt_slope_r* is a parameter related to the slope of the long tail in the low ``x`` values (the lower, the steeper) - *step_height_r* is the factor between the height of the step down and the gaussian height Parameters: ----------- - x: Independant variable where the functions are calculated. - len_x: Number of elements in the x array. - phypermet: Array of hypermet function parameters: *(area1, position1, fwhm1, st_area_r1, st_slope_r1, lt_area_r1, lt_slope_r1, step_height_r1, ...)* - len_phypermet: Number of elements in the phypermet array. Must be a multiple of 8. - y: Output array. Must have memory allocated for the same number of elements as x (len_x). - tail_flags: sum of binary flags to activate the various terms of the function: - 1 (b0001): Gaussian term - 2 (b0010): st term - 4 (b0100): lt term - 8 (b1000): step term E.g., to activate all termsof the hypermet, use ``tail_flags = 1 + 2 + 4 + 8 = 15`` Adapted from PyMca module SpecFitFuns */ int sum_fastahypermet(double* x, int len_x, double* phypermet, int len_phypermet, double* y, int tail_flags) { int i, j; int g_term_flag, st_term_flag, lt_term_flag, step_term_flag; double c1, c2, sigma, height, sigma_sqrt2, sqrt2PI, inv_2_sqrt_2_log2, x_minus_position, epsilon; double area, position, fwhm, st_area_r, st_slope_r, lt_area_r, lt_slope_r, step_height_r; if (test_params(len_phypermet, 8, "sum_hypermet", "height, centroid, fwhm, st_area_r, st_slope_r, lt_area_r, lt_slope_r, step_height_r")) { return(1); } g_term_flag = tail_flags & 1; st_term_flag = (tail_flags>>1) & 1; lt_term_flag = (tail_flags>>2) & 1; step_term_flag = (tail_flags>>3) & 1; /* Initialize output array */ for (j=0; j epsilon) && (x_minus_position / st_slope_r) <= 612) { c1 = st_area_r * 0.5 * \ erfc((x_minus_position/sigma_sqrt2) + 0.5 * sigma_sqrt2 / st_slope_r); y[j] += ((area * c1) / st_slope_r) * \ fastexp(0.5 * (sigma / st_slope_r) * (sigma / st_slope_r) +\ (x_minus_position / st_slope_r)); } /* lt term */ if (lt_term_flag && (fabs(lt_slope_r) > epsilon) && (x_minus_position / lt_slope_r) <= 612) { c1 = lt_area_r * \ 0.5 * erfc((x_minus_position/sigma_sqrt2) + 0.5 * sigma_sqrt2 / lt_slope_r); y[j] += ((area * c1) / lt_slope_r) * \ fastexp(0.5 * (sigma / lt_slope_r) * (sigma / lt_slope_r) +\ (x_minus_position / lt_slope_r)); } /* step term flag */ if (step_term_flag) { y[j] += step_height_r * (area / (sigma * sqrt2PI)) *\ 0.5 * erfc(x_minus_position / sigma_sqrt2); } } } return(0); } void pileup(double* x, long len_x, double* ret, int input2, double zero, double gain) { //int input2=0; //double zero=0.0; //double gain=1.0; int i, j, k; double *px, *pret, *pall; /* the pointer to the starting position of par data */ px = x; pret = ret; *pret = 0; k = (int )(zero/gain); for (i=input2; i= 0) { pret = (double *) ret+(i+k); for (j=0; j