[3570545] | 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 |
---|
[93f02d51] | 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; |
---|
[0003cc3] | 23 | |
---|
[d36facf] | 24 | double t = 1.0; |
---|
| 25 | double y = 1.0; |
---|
[93f02d51] | 26 | if (x < 0) |
---|
| 27 | sign = -1; |
---|
| 28 | x = fabs(x); |
---|
| 29 | |
---|
| 30 | // A&S formula 7.1.26 |
---|
[0003cc3] | 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); |
---|
[93f02d51] | 33 | |
---|
| 34 | return sign*y; |
---|
| 35 | } |
---|
| 36 | #endif |
---|
[3570545] | 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; |
---|
[d36facf] | 181 | double zhi, Ghi, erfhi, m, b; |
---|
[3570545] | 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 */ |
---|
[d36facf] | 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]; |
---|
[3570545] | 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; |
---|
[d36facf] | 238 | double sigma, Qo, limit; |
---|
[3570545] | 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. */ |
---|
[d36facf] | 247 | sigma = dQ[out]; |
---|
| 248 | Qo = Q[out]; |
---|
| 249 | limit = sqrt(-2.*sigma*sigma* LOG_RESLIMIT); |
---|
[3570545] | 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 | } |
---|