source: sasview/park-1.2.1/park/lib/resolution.c @ 93f02d51

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 93f02d51 was 93f02d51, checked in by Mathieu Doucet <doucetm@…>, 13 years ago

Re #5 fixing park compilation on MSVC

  • Property mode set to 100644
File size: 9.4 KB
Line 
1/* This program is public domain. */
2
3#include <stdio.h>
4#include <assert.h>
5#include <string.h>
6#include <math.h>
7#ifdef SGI
8#include <ieeefp.h>
9#endif
10#if defined(_MSC_VER)
11double erf(double x)
12{
13    // constants
14    double a1 =  0.254829592;
15    double a2 = -0.284496736;
16    double a3 =  1.421413741;
17    double a4 = -1.453152027;
18    double a5 =  1.061405429;
19    double p  =  0.3275911;
20
21    // Save the sign of x
22    int sign = 1;
23    if (x < 0)
24        sign = -1;
25    x = fabs(x);
26
27    // A&S formula 7.1.26
28    double t = 1.0/(1.0 + p*x);
29    double y = 1.0 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*exp(-x*x);
30
31    return sign*y;
32}
33#endif
34
35/* What to do at the endpoints --- USE_TRUNCATED_NORMALIZATION will
36 * avoid the assumption that the data is zero where it hasn't been
37 * measured.
38 */
39#define USE_TRUNCATED_NORMALIZATION
40
41/* Computed using extended precision with Octave's symbolic toolbox. */
42#define PI4          12.56637061435917295385
43#define PI_180        0.01745329251994329576
44#define LN256         5.54517744447956247533
45#define SQRT2         1.41421356237309504880
46#define SQRT2PI       2.50662827463100050241
47
48/* Choose the resolution limit based on relative contribution to resolution
49 * rather than absolute contribution.  So if we ignore everything below,
50 * e.g. 0.1% of the peak, that limit occurs when G(x)/G(0) = 0.001 for
51 * gaussian G of width sima, or when x = sqrt(-2 sigma^2 log(0.001)). */
52#define LOG_RESLIMIT -6.90775527898213703123
53
54/** \file
55The resolution function returns the convolution of the reflectometry
56curve with a Q-dependent gaussian.
57
58We provide the following function:
59   resolution(Nin, Qin, Rin, N, Q, dQ, R)  returns convolution
60
61where
62   Nin is the number of theory points
63   Qin,Rin are the computed theory points
64   N is the number of Q points to calculate
65   Q are the locations of the measured data points
66   dQ are the width (sigma) of the convolution at each measured point
67   R is the returned convolution.
68
69Note that FWHM = sqrt(8 ln 2) dQ, so scale dQ appropriately.
70
71The contribution of Q to a resolution of width dQo at point Qo is:
72
73   p(Q) = 1/sqrt(2 pi dQo^2) exp ( (Q-Qo)^2/(2 dQo^2) )
74
75We are approximating the convolution at Qo using a numerical
76approximation to the integral over the measured points.  For
77efficiency, the integral is limited to p(Q_i)/p(0)>=0.001. 
78
79Note that the function we are convoluting is falling off as Q^4.
80That means the correct convolution should uniformly sample across
81the entire width of the Gaussian.  This is not possible at the
82end points unless you calculate the reflectivity beyond what is
83strictly needed for the data. For a given dQ and step size,
84you need enough steps that the following is true:
85
86    (n*step)^2 > -2 dQ^2 * ln 0.001
87
88The choice of sampling density is particularly important near the
89critical edge.  This is where the resolution calculation has the
90largest effect on the reflectivity curve. In one particular model,
91calculating every 0.001 rather than every 0.02 changed one value
92above the critical edge by 15%.  This is likely to be a problem for
93any system with a well defined critical edge.  The solution is to
94compute the theory function over a finer mesh where the derivative
95is changing rapidly.  For the critical edge, I have found a sampling
96density of 0.005 to be good enough.
97
98For systems involving thick layers, the theory function oscillates
99rapidly around the measured points.  This is a problem when the
100period of the oscillation, 2 pi/d for total sample depth d, is on
101the order of the width of the resolution function. This is true even
102for gradually changing profiles in materials with very high roughness
103values.  In these systems, the theory function should be oversampled
104around the measured points Q.  With a single thick layer, oversampling
105can be limited to just one period.  With multiple thick layers,
106oscillations will show interference patterns and it will be necessary
107to oversample uniformly between the measured points.  When oversampled
108spacing is less than about 2 pi/7 d, it is possible to see aliasing
109effects. 
110
111FIXME is it better to use random sampling or strictly
112regular spacing when you are undersampling?
113
114===============================================================
115*/
116
117#undef USE_TRAPEZOID_RULE
118#ifdef USE_TRAPEZOID_RULE
119#warning This code does strange things with small sigma and large spacing
120/* FIXME trapezoid performs very badly for large spacing unless
121   we normalize to the unit width.  For very small sigma, the gaussian
122   is a spike, but we are approximating it by a triangle so it is
123   not surprising it works so poorly.  A slightly better solution is
124   to use the inner limits rather than the outer limits, but this will
125   still break down if the Q spacing is approximately equal to limit.
126   Best is to not use trapezoid.
127*/
128
129/* Trapezoid rule for numerical integration of convolution */
130double 
131convolve_point(const double Qin[], const double Rin[], int k, int n,
132        double Qo, double limit, double sigma)
133{
134  const double two_sigma_sq = 2. * sigma * sigma;
135  double z, Glo, RGlo, R, norm;
136 
137  z = Qo - Qin[k];
138  Glo = exp(-z*z/two_sigma_sq);
139  RGlo = Rin[k]*Glo;
140  norm = R = 0.;
141  while (++k < n) {
142    /* Compute the next endpoint */
143    const double zhi = Qo - Qin[k];
144    const double Ghi = exp(-zhi*zhi/two_sigma_sq);
145    const double RGhi = Rin[k] * Ghi;
146    const double halfstep = 0.5*(Qin[k] - Qin[k-1]);
147   
148    /* Add the trapezoidal area. */
149    norm += halfstep * (Ghi + Glo);
150    R += halfstep * (RGhi + RGlo);
151   
152    /* Save the endpoint for next trapezoid. */
153    Glo = Ghi;
154    RGlo = RGhi;
155   
156    /* Check if we've calculated far enough */
157    if (Qin[k] >= Qo+limit) break;
158  }
159
160  /* Scale to area of the linear spline distribution we actually used. */
161  return R / norm;
162
163  /* Scale to gaussian of unit area */
164  /* Fails badly for small sigma or large Q steps---do not use. */
165  /* return R / sigma*SQRT2PI; */
166}
167
168#else /* !USE_TRAPEZOID_RULE */
169
170/* Analytic convolution of gaussian with linear spline */
171/* More expensive but more reliable */
172double 
173convolve_point(const double Qin[], const double Rin[], int k, int n,
174               double Qo, double limit, double sigma)
175{
176  const double two_sigma_sq = 2. * sigma * sigma;
177  double z, Glo, erflo, erfmin, R;
178 
179  z = Qo - Qin[k];
180  Glo = exp(-z*z/two_sigma_sq);
181  erfmin = erflo = erf(-z/(SQRT2*sigma));
182  R = 0.;
183  /* printf("%5.3f: (%5.3f,%11.5g)",Qo,Qin[k],Rin[k]); */
184  while (++k < n) {
185        /* No additional contribution from duplicate points. */
186        if (Qin[k] == Qin[k-1]) continue;
187 
188    /* Compute the next endpoint */
189    const double zhi = Qo - Qin[k];
190    const double Ghi = exp(-zhi*zhi/two_sigma_sq);
191    const double erfhi = erf(-zhi/(SQRT2*sigma));
192    const double m = (Rin[k]-Rin[k-1])/(Qin[k]-Qin[k-1]);
193    const double b = Rin[k] - m * Qin[k];
194
195    /* Add the integrals. */
196    R += 0.5*(m*Qo+b)*(erfhi-erflo) - sigma/SQRT2PI*m*(Ghi-Glo);
197
198    /* Debug computation failures. */
199    /*
200    if isnan(R) {
201        printf("NaN from %d: zhi=%g, Ghi=%g, erfhi=%g, m=%g, b=%g\n",
202               k,zhi,Ghi,erfhi,m,b);
203    }
204    */
205   
206    /* Save the endpoint for next trapezoid. */
207    Glo = Ghi;
208    erflo = erfhi;
209   
210    /* Check if we've calculated far enough */
211    if (Qin[k] >= Qo+limit) break;
212  }
213  /* printf(" (%5.3f,%11.5g)",Qin[k<n?k:n-1],Rin[k<n?k:n-1]); */
214
215#ifdef USE_TRUNCATED_NORMALIZATION
216  /* Normalize by the area of the truncated gaussian */
217  /* At this point erflo = erfmax */
218  /* printf ("---> %11.5g\n",2*R/(erflo-erfmin)); */
219  return 2 * R / (erflo - erfmin);
220#else
221  /* Return unnormalized (we used a gaussian of unit area) */
222  /* printf ("---> %11.5g\n",R); */
223  return R;
224#endif
225}
226
227#endif /* !USE_TRAPEZOID_RULE */
228
229void
230resolution(int Nin, const double Qin[], const double Rin[],
231           int N, const double Q[], const double dQ[], double R[])
232{
233  int lo,out;
234
235  /* FIXME fails if Qin are not sorted; slow if Q not sorted */
236  assert(Nin>1);
237
238  /* Scan through all Q values to be calculated */
239  lo = 0;
240  for (out=0; out < N; out++) {
241    /* width of resolution window for Q is w = 2 dQ^2. */
242    const double sigma = dQ[out];
243    const double Qo = Q[out];
244    const double limit = sqrt(-2.*sigma*sigma* LOG_RESLIMIT);
245
246    /* if (out%20==0) printf("%d: Q,dQ = %g,%g\n",out,Qo,sigma); */
247
248    /* Line up the left edge of the convolution window */
249    /* It is probably forward from the current position, */
250    /* but if the next dQ is a lot higher than the current */
251    /* dQ or if the Q are not sorted, then it may be before */
252    /* the current position. */
253    /* FIXME verify that the convolution window is just right */
254    while (lo < Nin-1 && Qin[lo] < Qo-limit) lo++;
255    while (lo > 0 && Qin[lo] > Qo-limit) lo--;
256
257    /* Special handling to avoid 0/0 for w=0. */
258    if (sigma > 0.) {
259      R[out] = convolve_point(Qin,Rin,lo,Nin,Qo,limit,sigma);
260    } else if (lo < Nin-1) {
261      /* Linear interpolation */
262      double m = (Rin[lo+1]-Rin[lo])/(Qin[lo+1]-Qin[lo]);
263      double b = Rin[lo] - m*Qin[lo];
264      R[out] = m*Qo + b;
265    } else if (lo > 0) {
266      /* Linear extrapolation */
267      double m = (Rin[lo]-Rin[lo-1])/(Qin[lo]-Qin[lo-1]);
268      double b = Rin[lo] - m*Qin[lo];
269      R[out] = m*Qo + b;
270    } else {
271      /* Can't happen because there is more than one point in Qin. */
272      assert(Nin>1);
273    }
274  }
275
276}
Note: See TracBrowser for help on using the repository browser.