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) |
---|
11 | double 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 = 1.0; |
---|
25 | double y = 1.0; |
---|
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 |
---|
58 | The resolution function returns the convolution of the reflectometry |
---|
59 | curve with a Q-dependent gaussian. |
---|
60 | |
---|
61 | We provide the following function: |
---|
62 | resolution(Nin, Qin, Rin, N, Q, dQ, R) returns convolution |
---|
63 | |
---|
64 | where |
---|
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 | |
---|
72 | Note that FWHM = sqrt(8 ln 2) dQ, so scale dQ appropriately. |
---|
73 | |
---|
74 | The 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 | |
---|
78 | We are approximating the convolution at Qo using a numerical |
---|
79 | approximation to the integral over the measured points. For |
---|
80 | efficiency, the integral is limited to p(Q_i)/p(0)>=0.001. |
---|
81 | |
---|
82 | Note that the function we are convoluting is falling off as Q^4. |
---|
83 | That means the correct convolution should uniformly sample across |
---|
84 | the entire width of the Gaussian. This is not possible at the |
---|
85 | end points unless you calculate the reflectivity beyond what is |
---|
86 | strictly needed for the data. For a given dQ and step size, |
---|
87 | you need enough steps that the following is true: |
---|
88 | |
---|
89 | (n*step)^2 > -2 dQ^2 * ln 0.001 |
---|
90 | |
---|
91 | The choice of sampling density is particularly important near the |
---|
92 | critical edge. This is where the resolution calculation has the |
---|
93 | largest effect on the reflectivity curve. In one particular model, |
---|
94 | calculating every 0.001 rather than every 0.02 changed one value |
---|
95 | above the critical edge by 15%. This is likely to be a problem for |
---|
96 | any system with a well defined critical edge. The solution is to |
---|
97 | compute the theory function over a finer mesh where the derivative |
---|
98 | is changing rapidly. For the critical edge, I have found a sampling |
---|
99 | density of 0.005 to be good enough. |
---|
100 | |
---|
101 | For systems involving thick layers, the theory function oscillates |
---|
102 | rapidly around the measured points. This is a problem when the |
---|
103 | period of the oscillation, 2 pi/d for total sample depth d, is on |
---|
104 | the order of the width of the resolution function. This is true even |
---|
105 | for gradually changing profiles in materials with very high roughness |
---|
106 | values. In these systems, the theory function should be oversampled |
---|
107 | around the measured points Q. With a single thick layer, oversampling |
---|
108 | can be limited to just one period. With multiple thick layers, |
---|
109 | oscillations will show interference patterns and it will be necessary |
---|
110 | to oversample uniformly between the measured points. When oversampled |
---|
111 | spacing is less than about 2 pi/7 d, it is possible to see aliasing |
---|
112 | effects. |
---|
113 | |
---|
114 | FIXME is it better to use random sampling or strictly |
---|
115 | regular 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 */ |
---|
133 | double |
---|
134 | convolve_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 */ |
---|
175 | double |
---|
176 | convolve_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 | double zhi, Ghi, erfhi, m, b; |
---|
182 | |
---|
183 | z = Qo - Qin[k]; |
---|
184 | Glo = exp(-z*z/two_sigma_sq); |
---|
185 | erfmin = erflo = erf(-z/(SQRT2*sigma)); |
---|
186 | R = 0.; |
---|
187 | /* printf("%5.3f: (%5.3f,%11.5g)",Qo,Qin[k],Rin[k]); */ |
---|
188 | while (++k < n) { |
---|
189 | /* No additional contribution from duplicate points. */ |
---|
190 | if (Qin[k] == Qin[k-1]) continue; |
---|
191 | |
---|
192 | /* Compute the next endpoint */ |
---|
193 | zhi = Qo - Qin[k]; |
---|
194 | Ghi = exp(-zhi*zhi/two_sigma_sq); |
---|
195 | erfhi = erf(-zhi/(SQRT2*sigma)); |
---|
196 | m = (Rin[k]-Rin[k-1])/(Qin[k]-Qin[k-1]); |
---|
197 | b = Rin[k] - m * Qin[k]; |
---|
198 | |
---|
199 | /* Add the integrals. */ |
---|
200 | R += 0.5*(m*Qo+b)*(erfhi-erflo) - sigma/SQRT2PI*m*(Ghi-Glo); |
---|
201 | |
---|
202 | /* Debug computation failures. */ |
---|
203 | /* |
---|
204 | if isnan(R) { |
---|
205 | printf("NaN from %d: zhi=%g, Ghi=%g, erfhi=%g, m=%g, b=%g\n", |
---|
206 | k,zhi,Ghi,erfhi,m,b); |
---|
207 | } |
---|
208 | */ |
---|
209 | |
---|
210 | /* Save the endpoint for next trapezoid. */ |
---|
211 | Glo = Ghi; |
---|
212 | erflo = erfhi; |
---|
213 | |
---|
214 | /* Check if we've calculated far enough */ |
---|
215 | if (Qin[k] >= Qo+limit) break; |
---|
216 | } |
---|
217 | /* printf(" (%5.3f,%11.5g)",Qin[k<n?k:n-1],Rin[k<n?k:n-1]); */ |
---|
218 | |
---|
219 | #ifdef USE_TRUNCATED_NORMALIZATION |
---|
220 | /* Normalize by the area of the truncated gaussian */ |
---|
221 | /* At this point erflo = erfmax */ |
---|
222 | /* printf ("---> %11.5g\n",2*R/(erflo-erfmin)); */ |
---|
223 | return 2 * R / (erflo - erfmin); |
---|
224 | #else |
---|
225 | /* Return unnormalized (we used a gaussian of unit area) */ |
---|
226 | /* printf ("---> %11.5g\n",R); */ |
---|
227 | return R; |
---|
228 | #endif |
---|
229 | } |
---|
230 | |
---|
231 | #endif /* !USE_TRAPEZOID_RULE */ |
---|
232 | |
---|
233 | void |
---|
234 | resolution(int Nin, const double Qin[], const double Rin[], |
---|
235 | int N, const double Q[], const double dQ[], double R[]) |
---|
236 | { |
---|
237 | int lo,out; |
---|
238 | double sigma, Qo, limit; |
---|
239 | |
---|
240 | /* FIXME fails if Qin are not sorted; slow if Q not sorted */ |
---|
241 | assert(Nin>1); |
---|
242 | |
---|
243 | /* Scan through all Q values to be calculated */ |
---|
244 | lo = 0; |
---|
245 | for (out=0; out < N; out++) { |
---|
246 | /* width of resolution window for Q is w = 2 dQ^2. */ |
---|
247 | sigma = dQ[out]; |
---|
248 | Qo = Q[out]; |
---|
249 | limit = sqrt(-2.*sigma*sigma* LOG_RESLIMIT); |
---|
250 | |
---|
251 | /* if (out%20==0) printf("%d: Q,dQ = %g,%g\n",out,Qo,sigma); */ |
---|
252 | |
---|
253 | /* Line up the left edge of the convolution window */ |
---|
254 | /* It is probably forward from the current position, */ |
---|
255 | /* but if the next dQ is a lot higher than the current */ |
---|
256 | /* dQ or if the Q are not sorted, then it may be before */ |
---|
257 | /* the current position. */ |
---|
258 | /* FIXME verify that the convolution window is just right */ |
---|
259 | while (lo < Nin-1 && Qin[lo] < Qo-limit) lo++; |
---|
260 | while (lo > 0 && Qin[lo] > Qo-limit) lo--; |
---|
261 | |
---|
262 | /* Special handling to avoid 0/0 for w=0. */ |
---|
263 | if (sigma > 0.) { |
---|
264 | R[out] = convolve_point(Qin,Rin,lo,Nin,Qo,limit,sigma); |
---|
265 | } else if (lo < Nin-1) { |
---|
266 | /* Linear interpolation */ |
---|
267 | double m = (Rin[lo+1]-Rin[lo])/(Qin[lo+1]-Qin[lo]); |
---|
268 | double b = Rin[lo] - m*Qin[lo]; |
---|
269 | R[out] = m*Qo + b; |
---|
270 | } else if (lo > 0) { |
---|
271 | /* Linear extrapolation */ |
---|
272 | double m = (Rin[lo]-Rin[lo-1])/(Qin[lo]-Qin[lo-1]); |
---|
273 | double b = Rin[lo] - m*Qin[lo]; |
---|
274 | R[out] = m*Qo + b; |
---|
275 | } else { |
---|
276 | /* Can't happen because there is more than one point in Qin. */ |
---|
277 | assert(Nin>1); |
---|
278 | } |
---|
279 | } |
---|
280 | |
---|
281 | } |
---|