source: sasview/park-1.2.1/park/lib/resolution.c @ 0003cc3

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 0003cc3 was 0003cc3, checked in by Mathieu Doucet <doucetm@…>, 13 years ago

Re #5 fixing park compilation on MSVC

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