1 | /** |
---|
2 | * Spherical Bessel function 3*j1(x)/x |
---|
3 | * |
---|
4 | * Used for low q to avoid cancellation error. |
---|
5 | * Note that the values differ from sasview ~ 5e-12 rather than 5e-14, but |
---|
6 | * in this case it is likely cancellation errors in the original expression |
---|
7 | * using double precision that are the source. |
---|
8 | */ |
---|
9 | double sas_3j1x_x(double q); |
---|
10 | |
---|
11 | // The choice of the number of terms in the series and the cutoff value for |
---|
12 | // switching between series and direct calculation depends on the numeric |
---|
13 | // precision. |
---|
14 | // |
---|
15 | // Point where direct calculation reaches machine precision: |
---|
16 | // |
---|
17 | // single machine precision eps 3e-8 at qr=1.1 ** |
---|
18 | // double machine precision eps 4e-16 at qr=1.1 |
---|
19 | // |
---|
20 | // Point where Taylor series reaches machine precision (eps), where taylor |
---|
21 | // series matches direct calculation (cross) and the error at that point: |
---|
22 | // |
---|
23 | // prec n eps cross error |
---|
24 | // single 3 0.28 0.4 6.2e-7 |
---|
25 | // single 4 0.68 0.7 2.3e-7 |
---|
26 | // single 5 1.18 1.2 7.5e-8 |
---|
27 | // double 3 0.01 0.03 2.3e-13 |
---|
28 | // double 4 0.06 0.1 3.1e-14 |
---|
29 | // double 5 0.16 0.2 5.0e-15 |
---|
30 | // |
---|
31 | // ** Note: relative error on single precision starts increase on the direct |
---|
32 | // method at qr=1.1, rising from 3e-8 to 5e-5 by qr=1e3. This should be |
---|
33 | // safe for the sans range, with objects of 100 nm supported to a q of 0.1 |
---|
34 | // while maintaining 5 digits of precision. For usans/sesans, the objects |
---|
35 | // are larger but the q is smaller, so again it should be fine. |
---|
36 | // |
---|
37 | // See explore/sph_j1c.py for code to explore these ranges. |
---|
38 | |
---|
39 | // Use 4th order series |
---|
40 | #if FLOAT_SIZE>4 |
---|
41 | #define SPH_J1C_CUTOFF 0.1 |
---|
42 | #else |
---|
43 | #define SPH_J1C_CUTOFF 0.7 |
---|
44 | #endif |
---|
45 | |
---|
46 | double sas_3j1x_x(double q) |
---|
47 | { |
---|
48 | // 2017-05-18 PAK - support negative q |
---|
49 | if (fabs(q) < SPH_J1C_CUTOFF) { |
---|
50 | const double q2 = q*q; |
---|
51 | return (1.0 + q2*(-3./30. + q2*(3./840. + q2*(-3./45360.))));// + q2*(3./3991680.))))); |
---|
52 | } else { |
---|
53 | double sin_q, cos_q; |
---|
54 | SINCOS(q, sin_q, cos_q); |
---|
55 | return 3.0*(sin_q/q - cos_q)/(q*q); |
---|
56 | } |
---|
57 | } |
---|