source: sasview/park-1.2.1/park/lib/resolution.c @ 72af855

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

Adding park Part 2

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