source: sasview/src/sas/sascalc/calculator/c_extensions/librefl.c @ 12acdcc

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 12acdcc was b8080e1, checked in by Piotr Rozyczko <rozyczko@…>, 6 years ago

cherry picking sascalc changes from master SASVIEW-996
minor unit test fixes

  • Property mode set to 100644
File size: 8.8 KB
Line 
1// The original code, of which work was not DANSE funded,
2// was provided by J. Cho.
3// And modified to fit sansmodels/sansview: JC
4
5#include <math.h>
6#include "librefl.h"
7#include <stdio.h>
8#include <stdlib.h>
9#if defined _MSC_VER  || defined __TINYCC__
10#define NEED_ERF
11#endif
12
13
14
15#if defined(NEED_ERF)
16/* erf.c  - public domain implementation of error function erf(3m)
17
18reference - Haruhiko Okumura: C-gengo niyoru saishin algorithm jiten
19            (New Algorithm handbook in C language) (Gijyutsu hyouron
20            sha, Tokyo, 1991) p.227 [in Japanese]                 */
21
22
23#ifdef __TINYCC__
24# ifdef isnan
25#   undef isnan
26# endif
27# ifdef isfinite
28#   undef isfinite
29# endif
30# define isnan(x) (x != x)
31# define isfinite(x) (x != INFINITY && x != -INFINITY)
32#elif defined _WIN32
33# include <float.h>
34# if !defined __MINGW32__ || defined __NO_ISOCEXT
35#  ifndef isnan
36#   define isnan(x) _isnan(x)
37#  endif
38#  ifndef isinf
39#   define isinf(x) (!_finite(x) && !_isnan(x))
40#  endif
41#  ifndef isfinite
42#   define isfinite(x) _finite(x)
43#  endif
44# endif
45#endif
46
47static double q_gamma(double, double, double);
48
49/* Incomplete gamma function
50   1 / Gamma(a) * Int_0^x exp(-t) t^(a-1) dt  */
51static double p_gamma(double a, double x, double loggamma_a)
52{
53    int k;
54    double result, term, previous;
55
56    if (x >= 1 + a) return 1 - q_gamma(a, x, loggamma_a);
57    if (x == 0)     return 0;
58    result = term = exp(a * log(x) - x - loggamma_a) / a;
59    for (k = 1; k < 1000; k++) {
60        term *= x / (a + k);
61        previous = result;  result += term;
62        if (result == previous) return result;
63    }
64    fprintf(stderr, "erf.c:%d:p_gamma() could not converge.", __LINE__);
65    return result;
66}
67
68/* Incomplete gamma function
69   1 / Gamma(a) * Int_x^inf exp(-t) t^(a-1) dt  */
70static double q_gamma(double a, double x, double loggamma_a)
71{
72    int k;
73    double result, w, temp, previous;
74    double la = 1, lb = 1 + x - a;  /* Laguerre polynomial */
75
76    if (x < 1 + a) return 1 - p_gamma(a, x, loggamma_a);
77    w = exp(a * log(x) - x - loggamma_a);
78    result = w / lb;
79    for (k = 2; k < 1000; k++) {
80        temp = ((k - 1 - a) * (lb - la) + (k + x) * lb) / k;
81        la = lb;  lb = temp;
82        w *= (k - 1 - a) / k;
83        temp = w / (la * lb);
84        previous = result;  result += temp;
85        if (result == previous) return result;
86    }
87    fprintf(stderr, "erf.c:%d:q_gamma() could not converge.", __LINE__);
88    return result;
89}
90
91#define LOG_PI_OVER_2 0.572364942924700087071713675675 /* log_e(PI)/2 */
92
93double erf(double x)
94{
95    if (!isfinite(x)) {
96        if (isnan(x)) return x;      /* erf(NaN)   = NaN   */
97        return (x>0 ? 1.0 : -1.0);   /* erf(+-inf) = +-1.0 */
98    }
99    if (x >= 0) return   p_gamma(0.5, x * x, LOG_PI_OVER_2);
100    else        return - p_gamma(0.5, x * x, LOG_PI_OVER_2);
101}
102
103double erfc(double x)
104{
105    if (!isfinite(x)) {
106        if (isnan(x)) return x;      /* erfc(NaN)   = NaN      */
107        return (x>0 ? 0.0 : 2.0);    /* erfc(+-inf) = 0.0, 2.0 */
108    }
109    if (x >= 0) return  q_gamma(0.5, x * x, LOG_PI_OVER_2);
110    else        return  1 + p_gamma(0.5, x * x, LOG_PI_OVER_2);
111}
112#endif // NEED_ERF
113
114void cassign(Cplx *x, double real, double imag)
115{
116        x->re = real;
117        x->im = imag;
118}
119
120
121void cplx_add(Cplx *z, Cplx x, Cplx y)
122{
123        z->re = x.re + y.re;
124        z->im = x.im + y.im;
125}
126
127void rcmult(Cplx *z, double x, Cplx y)
128{
129        z->re = x*y.re;
130        z->im = x*y.im;
131}
132
133void cplx_sub(Cplx *z, Cplx x, Cplx y)
134{
135        z->re = x.re - y.re;
136        z->im = x.im - y.im;
137}
138
139
140void cplx_mult(Cplx *z, Cplx x, Cplx y)
141{
142        z->re = x.re*y.re - x.im*y.im;
143        z->im = x.re*y.im + x.im*y.re;
144}
145
146void cplx_div(Cplx *z, Cplx x, Cplx y)
147{
148        z->re = (x.re*y.re + x.im*y.im)/(y.re*y.re + y.im*y.im);
149        z->im = (x.im*y.re - x.re*y.im)/(y.re*y.re + y.im*y.im);
150}
151
152void cplx_exp(Cplx *z, Cplx b)
153{
154        double br,bi;
155        br=b.re;
156        bi=b.im;
157        z->re = exp(br)*cos(bi);
158        z->im = exp(br)*sin(bi);
159}
160
161
162void cplx_sqrt(Cplx *c, Cplx z)    //see Schaum`s Math Handbook p. 22, 6.6 and 6.10
163{
164        double zr,zi,x,y,r,w;
165
166        zr=z.re;
167        zi=z.im;
168
169        if (zr==0.0 && zi==0.0)
170        {
171                c->re=0.0;
172                c->im=0.0;
173        } else {
174                x=fabs(zr);
175                y=fabs(zi);
176                if (x>y)
177                {
178                        r=y/x;
179                        w=sqrt(x)*sqrt(0.5*(1.0+sqrt(1.0+r*r)));
180                } else {
181                        r=x/y;
182                        w=sqrt(y)*sqrt(0.5*(r+sqrt(1.0+r*r)));
183                }
184                if (zr >=0.0)
185                {
186                        c->re=w;
187                        c->im=zi/(2.0*w);
188                } else {
189                        c->im=(zi >= 0) ? w : -w;
190                        c->re=zi/(2.0*c->im);
191                }
192        }
193}
194
195void cplx_cos(Cplx *z, Cplx b)
196{
197        // cos(b) = (e^bi + e^-bi)/2
198        //        = (e^b.im e^-i bi.re) + e^-b.im e^i b.re)/2
199        //        = (e^b.im cos(-b.re) + e^b.im sin(-b.re) i)/2 + (e^-b.im cos(b.re) + e^-b.im sin(b.re) i)/2
200        //        = e^b.im cos(b.re)/2 - e^b.im sin(b.re)/2 i + 1/e^b.im cos(b.re)/2 + 1/e^b.im sin(b.re)/2 i
201        //        = (e^b.im + 1/e^b.im)/2 cos(b.re) + (-e^b.im + 1/e^b.im)/2 sin(b.re) i
202        //        = cosh(b.im) cos(b.re) - sinh(b.im) sin(b.re) i
203        double exp_b_im = exp(b.im);
204        z->re = 0.5*(+exp_b_im + 1.0/exp_b_im) * cos(b.re);
205        z->im = -0.5*(exp_b_im - 1.0/exp_b_im) * sin(b.re);
206}
207
208// normalized and modified erf
209//   |
210// 1 +                __  - - - -
211//   |             _
212//       |            _
213//   |        __
214// 0 + - - -
215//   |-------------+------------+--
216//   0           center       n_sub    --->
217//                                     ind
218//
219// n_sub = total no. of bins(or sublayers)
220// ind = x position: 0 to max
221// nu = max x to integration
222double err_mod_func(double n_sub, double ind, double nu)
223{
224  double center, func;
225  if (nu == 0.0)
226                nu = 1e-14;
227        if (n_sub == 0.0)
228                n_sub = 1.0;
229
230
231        //ind = (n_sub-1.0)/2.0-1.0 +ind;
232        center = n_sub/2.0;
233        // transform it so that min(ind) = 0
234        ind -= center;
235        // normalize by max limit
236        ind /= center;
237        // divide by sqrt(2) to get Gaussian func
238        nu /= sqrt(2.0);
239        ind *= nu;
240        // re-scale and normalize it so that max(erf)=1, min(erf)=0
241        func = erf(ind)/erf(nu)/2.0;
242        // shift it by +0.5 in y-direction so that min(erf) = 0
243        func += 0.5;
244
245        return func;
246}
247double linearfunc(double n_sub, double ind, double nu)
248{
249  double bin_size, func;
250        if (n_sub == 0.0)
251                n_sub = 1.0;
252
253        bin_size = 1.0/n_sub;  //size of each sub-layer
254        // rescale
255        ind *= bin_size;
256        func = ind;
257
258        return func;
259}
260// use the right hand side from the center of power func
261double power_r(double n_sub, double ind, double nu)
262{
263  double bin_size,func;
264        if (nu == 0.0)
265                nu = 1e-14;
266        if (n_sub == 0.0)
267                n_sub = 1.0;
268
269        bin_size = 1.0/n_sub;  //size of each sub-layer
270        // rescale
271        ind *= bin_size;
272        func = pow(ind, nu);
273
274        return func;
275}
276// use the left hand side from the center of power func
277double power_l(double n_sub, double ind, double nu)
278{
279  double bin_size, func;
280        if (nu == 0.0)
281                nu = 1e-14;
282        if (n_sub == 0.0)
283                n_sub = 1.0;
284
285        bin_size = 1.0/n_sub;  //size of each sub-layer
286        // rescale
287        ind *= bin_size;
288        func = 1.0-pow((1.0-ind),nu);
289
290        return func;
291}
292// use 1-exp func from x=0 to x=1
293double exp_r(double n_sub, double ind, double nu)
294{
295  double bin_size, func;
296        if (nu == 0.0)
297                nu = 1e-14;
298        if (n_sub == 0.0)
299                n_sub = 1.0;
300
301        bin_size = 1.0/n_sub;  //size of each sub-layer
302        // rescale
303        ind *= bin_size;
304        // modify func so that func(0) =0 and func(max)=1
305        func = 1.0-exp(-nu*ind);
306        // normalize by its max
307        func /= (1.0-exp(-nu));
308
309        return func;
310}
311
312// use the left hand side mirror image of exp func
313double exp_l(double n_sub, double ind, double nu)
314{
315  double bin_size, func;
316        if (nu == 0.0)
317                nu = 1e-14;
318        if (n_sub == 0.0)
319                n_sub = 1.0;
320
321        bin_size = 1.0/n_sub;  //size of each sub-layer
322        // rescale
323        ind *= bin_size;
324        // modify func
325        func = exp(-nu*(1.0-ind))-exp(-nu);
326        // normalize by its max
327        func /= (1.0-exp(-nu));
328
329        return func;
330}
331
332// To select function called
333// At nu = 0 (singular point), call line function
334double intersldfunc(int fun_type, double n_sub, double i, double nu, double sld_l, double sld_r)
335{
336        double sld_i, func;
337        // this condition protects an error from the singular point
338        if (nu == 0.0){
339                nu = 1e-13;
340        }
341        // select func
342        switch(fun_type){
343                case 1 :
344                        func = power_r(n_sub, i, nu);
345                        break;
346                case 2 :
347                        func = power_l(n_sub, i, nu);
348                        break;
349                case 3 :
350                        func = exp_r(n_sub, i, nu);
351                        break;
352                case 4 :
353                        func = exp_l(n_sub, i, nu);
354                        break;
355                case 5 :
356                        func = linearfunc(n_sub, i, nu);
357                        break;
358                default:
359                        func = err_mod_func(n_sub, i, nu);
360                        break;
361        }
362        // compute sld
363        if (sld_r>sld_l){
364                sld_i = (sld_r-sld_l)*func+sld_l; //sld_cal(sld[i],sld[i+1],n_sub,dz,thick);
365        }
366        else if (sld_r<sld_l){
367                func = 1.0-func;
368                sld_i = (sld_l-sld_r)*func+sld_r; //sld_cal(sld[i],sld[i+1],n_sub,dz,thick);
369        }
370        else{
371                sld_i = sld_r;
372        }
373        return sld_i;
374}
375
376
377// used by refl.c
378double interfunc(int fun_type, double n_sub, double i, double sld_l, double sld_r)
379{
380        double sld_i, func;
381        switch(fun_type){
382                case 0 :
383                        func = err_mod_func(n_sub, i, 2.5);
384                        break;
385                default:
386                        func = linearfunc(n_sub, i, 1.0);
387                        break;
388        }
389        if (sld_r>sld_l){
390                sld_i = (sld_r-sld_l)*func+sld_l; //sld_cal(sld[i],sld[i+1],n_sub,dz,thick);
391        }
392        else if (sld_r<sld_l){
393                func = 1.0-func;
394                sld_i = (sld_l-sld_r)*func+sld_r; //sld_cal(sld[i],sld[i+1],n_sub,dz,thick);
395        }
396        else{
397                sld_i = sld_r;
398        }
399        return sld_i;
400}
Note: See TracBrowser for help on using the repository browser.