source: sasview/DataLoader/extensions/smearer.cpp @ 8f7936f

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 8f7936f was cd2ced80, checked in by Jae Cho <jhjcho@…>, 14 years ago

new algorithm for slit smearing and its test

  • Property mode set to 100644
File size: 11.2 KB
RevLine 
[a3f8d58]1/**
2        This software was developed by the University of Tennessee as part of the
3        Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
4        project funded by the US National Science Foundation.
5
6        If you use DANSE applications to do scientific research that leads to
7        publication, we ask that you acknowledge the use of the software with the
8        following sentence:
9
10        "This work benefited from DANSE software developed under NSF award DMR-0520547."
11
12        copyright 2009, University of Tennessee
13 */
14#include "smearer.hh"
15#include <stdio.h>
16#include <math.h>
17using namespace std;
18
19
20/**
21 * Constructor for BaseSmearer
22 *
23 * @param qmin: minimum Q value
24 * @param qmax: maximum Q value
25 * @param nbins: number of Q bins
26 */
27BaseSmearer :: BaseSmearer(double qmin, double qmax, int nbins) {
28        // Number of bins
29        this->nbins = nbins;
30        this->qmin = qmin;
31        this->qmax = qmax;
32        // Flag to keep track of whether we have a smearing matrix or
33        // whether we need to compute one
34        has_matrix = false;
[5859862]35        even_binning = true;
36};
37
38/**
39 * Constructor for BaseSmearer
40 *
41 * Used for uneven binning
42 * @param q: array of Q values
43 * @param nbins: number of Q bins
44 */
45BaseSmearer :: BaseSmearer(double* q, int nbins) {
46        // Number of bins
47        this->nbins = nbins;
48        this->q_values = q;
49        // Flag to keep track of whether we have a smearing matrix or
50        // whether we need to compute one
51        has_matrix = false;
52        even_binning = false;
[a3f8d58]53};
54
55/**
56 * Constructor for SlitSmearer
57 *
58 * @param width: slit width in Q units
59 * @param height: slit height in Q units
60 * @param qmin: minimum Q value
61 * @param qmax: maximum Q value
[5859862]62 * @param nbins: number of Q bins
[a3f8d58]63 */
64SlitSmearer :: SlitSmearer(double width, double height, double qmin, double qmax, int nbins) :
65        BaseSmearer(qmin, qmax, nbins){
66        this->height = height;
67        this->width = width;
68};
69
[5859862]70/**
71 * Constructor for SlitSmearer
72 *
73 * @param width: slit width in Q units
74 * @param height: slit height in Q units
75 * @param q: array of Q values
76 * @param nbins: number of Q bins
77 */
78SlitSmearer :: SlitSmearer(double width, double height, double* q, int nbins) :
79        BaseSmearer(q, nbins){
80        this->height = height;
81        this->width = width;
82};
83
84/**
85 * Constructor for QSmearer
86 *
87 * @param width: array slit widths for each Q point, in Q units
88 * @param qmin: minimum Q value
89 * @param qmax: maximum Q value
90 * @param nbins: number of Q bins
91 */
[a3f8d58]92QSmearer :: QSmearer(double* width, double qmin, double qmax, int nbins) :
93        BaseSmearer(qmin, qmax, nbins){
94        this->width = width;
95};
96
97/**
[5859862]98 * Constructor for QSmearer
99 *
100 * @param width: array slit widths for each Q point, in Q units
101 * @param q: array of Q values
102 * @param nbins: number of Q bins
103 */
104QSmearer :: QSmearer(double* width, double* q, int nbins) :
105        BaseSmearer(q, nbins){
106        this->width = width;
107};
108
109/**
110 * Compute the slit smearing matrix
111 *
112 * For even binning (q_min to q_max with nbins):
113 *
114 *   step = (q_max-q_min)/(nbins-1)
115 *   first bin goes from q_min to q_min+step
116 *   last bin goes from q_max to q_max+step
117 *
118 * For binning according to q array:
119 *
120 * Each q point represents a bin going from half the distance between it
121 * and the previous point to half the distance between it and the next point.
122 *
123 *    Example: bin i goes from (q_values[i-1]+q_values[i])/2 to (q_values[i]+q_values[i+1])/2
124 *
125 * The exceptions are the first and last bins, which are centered at the first and
126 * last q-values, respectively. The width of the first and last bins is the distance between
[cd2ced80]127 * their respective neighboring q-value.
[a3f8d58]128 */
129void SlitSmearer :: compute_matrix(){
130
131        weights = new vector<double>(nbins*nbins,0);
132
[5859862]133        // Check the length of the data
134        if (nbins<2) return;
[cd2ced80]135        int npts_h = height>0.0 ? npts : 1;
136        int npts_w = width>0.0 ? npts : 1;
137
138        // If both height and width are great than zero,
139        // modify the number of points in each direction so
140        // that the total number of points is still what
141        // the user would expect (downgrade resolution)
142        //if(npts_h>1 && npts_w>1){
143        //      npts_h = (int)ceil(sqrt((double)npts));
144        //      npts_w = npts_h;
145        //}
146        double shift_h, shift_w, hbin_size, wbin_size;
147        // Make sure height and width are all positive (FWMH/2)
148        // Assumption; height and width are all same for all q points
149        if(npts_h == 1){
150                shift_h = 0.0;
151        } else {
152                shift_h = fabs(height);
153        }
154        if(npts_w == 1){
155                shift_w = 0.0;
156        } else {
157                shift_w = fabs(width);
158        }
159        // size of the h bin and w bin
160        hbin_size = shift_h / nbins;
161        wbin_size = shift_w / nbins;
[5859862]162
[a3f8d58]163        // Loop over all q-values
164        for(int i=0; i<nbins; i++) {
[cd2ced80]165                // Find Weights
166                // Find q where the resolution smearing calculation of I(q) occurs
167                double q, q_min, q_max, q_0;
[5859862]168                get_bin_range(i, &q, &q_min, &q_max);
[cd2ced80]169                bool last_qpoint = true;
170                // Find q[0] value to normalize the weight later,
171                //  otherwise, we will have a precision problem.
172                if (i == 0){
173                        q_0 = q;
[a3f8d58]174                }
[cd2ced80]175                // Loop over all qj-values
176                bool first_w = true;
177                for(int j=0; j<nbins; j++) {
178                        double q_j, q_high, q_low;
179                        // Calculate bin size of q_j
180                        get_bin_range(j, &q_j, &q_low, &q_high);
181                        // Check q_low that can not be negative.
182                        if (q_low < 0.0){
183                                q_low = 0.0;
184                        }
185                        // default parameter values
186                        (*weights)[i*nbins+j] = 0.0;
187                        double shift_w = 0.0;
188                        // Condition: zero slit smear.
189                        if (npts_w == 1 && npts_h == 1){
190                                if(q_j == q) {
191                                        (*weights)[i*nbins+j] = 1.0;
192                                }
[a3f8d58]193                        }
[cd2ced80]194                        //Condition:Smear weight integration for width >0 when the height (=0) does not present.
195                        //Or height << width.
196                        else if((npts_w!=1 && npts_h == 1)|| (npts_w!=1 && npts_h != 1 && width/height > 100.0)){
197                                shift_w = width;
198                                //del_w = width/((double)npts_w-1.0);
199                                double q_shifted_low = q - shift_w;
200                                // High limit of the resolution range
201                                double q_shifted_high = q + shift_w;
202                                // Go through all the q_js for weighting those points
203                                if(q_j >= q_shifted_low && q_j <= q_shifted_high) {
204                                        // The weighting factor comes,
205                                        // Give some weight (delq_bin) for the q_j within the resolution range
206                                        // Weight should be same for all qs except
207                                        // for the q bin size at j.
208                                        // Note that the division by q_0 is only due to the precision problem
209                                        // where q_high - q_low gets to very small.
210                                        // Later, it will be normalized again.
211                                        (*weights)[i*nbins+j] += (q_high - q_low)/q_0 ;
[a3f8d58]212                                }
[cd2ced80]213                        }
214                        else{
215                                // Loop for width (;Height is analytical.)
216                                // Condition: height >>> width, otherwise, below is not accurate enough.
217                                // Smear weight numerical iteration for width >0 when the height (>0) presents.
218                                // When width = 0, the numerical iteration will be skipped.
219                                // The resolution calculation for the height is done by direct integration,
220                                // assuming the I(q'=sqrt(q_j^2-(q+shift_w)^2)) is constant within a q' bin, [q_high, q_low].
221                                // In general, this weight numerical iteration for width >0 might be a rough approximation,
222                                // but it must be good enough when height >>> width.
223                                for(int k=(-npts_w + 1); k<npts_w; k++){
224                                        if(npts_w!=1){
225                                                shift_w = width/((double)npts_w-1.0)*(double)k;
226                                        }
227                                        // For each q-value, compute the weight of each other q-bin
228                                        // in the I(q) array
229                                        // Low limit of the resolution range
230                                        double q_shift = q + shift_w;
231                                        if (q_shift < 0.0){
232                                                q_shift = 0.0;
233                                        }
234                                        double q_shifted_low = q_shift;
235                                        // High limit of the resolution range
236                                        double q_shifted_high = sqrt(q_shift * q_shift + shift_h * shift_h);
237
238
239                                        // Go through all the q_js for weighting those points
240                                        if(q_j >= q_shifted_low && q_j <= q_shifted_high) {
241                                                // The weighting factor comes,
242                                                // Give some weight (delq_bin) for the q_j within the resolution range
243                                                // Weight should be same for all qs except
244                                                // for the q bin size at j.
245                                                // Note that the division by q_0 is only due to the precision problem
246                                                // where q_high - q_low gets to very small.
247                                                // Later, it will be normalized again.
[5859862]248
[cd2ced80]249                                                double q_shift_min = q - width;
250
251                                                double u = (q_j * q_j - (q_shift) * (q_shift));
252                                                // The fabs below are not necessary but in case: the weight should never be imaginary.
253                                                // At the edge of each sub_width. weight += u(at q_high bin) - u(0), where u(0) = 0,
254                                                // and weighted by (2.0* npts_w -1.0)once for each q.
255                                                if (q == q_j) {
256                                                        if (k==0)
257                                                                (*weights)[i*nbins+j] += (sqrt(fabs((q_high)*(q_high)-q_shift * q_shift)))/q_0 * (2.0*double(npts_w)-1.0);
258                                                }
259                                                // For the rest of sub_width. weight += u(at q_high bin) - u(at q_low bin)
260                                                else if (u > 0.0){
261                                                        (*weights)[i*nbins+j] += (sqrt(fabs((q_high)*(q_high)- q_shift * q_shift))-sqrt(fabs((q_low)*(q_low)- q_shift * q_shift)))/q_0 ;
[5859862]262                                                }
263                                        }
264                                }
[a3f8d58]265                        }
266                }
267        }
268};
269
270/**
[cd2ced80]271 * Compute the point smearing matrix
[a3f8d58]272 */
273void QSmearer :: compute_matrix(){
274        weights = new vector<double>(nbins*nbins,0);
275
276        // Loop over all q-values
277        double step = (qmax-qmin)/((double)nbins-1.0);
[5859862]278        double q, q_min, q_max;
279        double q_j, q_jmax, q_jmin;
[a3f8d58]280        for(int i=0; i<nbins; i++) {
[5859862]281                get_bin_range(i, &q, &q_min, &q_max);
[a3f8d58]282
283                for(int j=0; j<nbins; j++) {
[5859862]284                        get_bin_range(j, &q_j, &q_jmin, &q_jmax);
[a3f8d58]285
286                        // Compute the fraction of the Gaussian contributing
[f867cd9]287                        // to the q_j bin between q_jmin and q_jmax
288                        double value =  erf( (q_jmax-q)/(sqrt(2.0)*width[i]) );
289                value -= erf( (q_jmin-q)/(sqrt(2.0)*width[i]) );
[a3f8d58]290                (*weights)[i*nbins+j] += value;
291                }
292        }
293}
294
295/**
[5859862]296 * Computes the Q range of a given bin of the Q distribution.
297 * The range is computed according the the data distribution that
298 * was given to the object at initialization.
299 *
300 * @param i: number of the bin in the distribution
301 * @param q: q-value of bin i
302 * @param q_min: lower bound of the bin
303 * @param q_max: higher bound of the bin
[cd2ced80]304 *
[5859862]305 */
[65883cf]306int BaseSmearer :: get_bin_range(int i, double* q, double* q_min, double* q_max) {
[5859862]307        if (even_binning) {
308                double step = (qmax-qmin)/((double)nbins-1.0);
309                *q = qmin + (double)i*step;
310                *q_min = *q - 0.5*step;
311                *q_max = *q + 0.5*step;
[65883cf]312                return 1;
313        } else if (i>=0 && i<nbins) {
[5859862]314                *q = q_values[i];
315                if (i==0) {
316                        double step = (q_values[1]-q_values[0])/2.0;
317                        *q_min = *q - step;
318                        *q_max = *q + step;
319                } else if (i==nbins-1) {
320                        double step = (q_values[i]-q_values[i-1])/2.0;
321                        *q_min = *q - step;
322                        *q_max = *q + step;
323                } else {
324                        *q_min = *q - (q_values[i]-q_values[i-1])/2.0;
325                        *q_max = *q + (q_values[i+1]-q_values[i])/2.0;
326                }
[65883cf]327                return 1;
[5859862]328        }
[65883cf]329        return -1;
[5859862]330}
331
332/**
[cd2ced80]333 * Perform smearing by applying the smearing matrix to the input Q array
[a3f8d58]334 */
335void BaseSmearer :: smear(double *iq_in, double *iq_out, int first_bin, int last_bin){
336
337        // If we haven't computed the smearing matrix, do it now
338        if(!has_matrix) {
339                compute_matrix();
340                has_matrix = true;
341        }
342
343        // Loop over q-values and multiply apply matrix
344        for(int q_i=first_bin; q_i<=last_bin; q_i++){
345                double sum = 0.0;
346                double counts = 0.0;
347
348                for(int i=first_bin; i<=last_bin; i++){
[cd2ced80]349                        // Skip if weight is less than 1e-03(this value is much smaller than
[f867cd9]350                        // the weight at the 3*sigma distance
351                        // Will speed up a little bit...
[cd2ced80]352                        if ((*weights)[q_i*nbins+i] < 1.0e-003){
[f867cd9]353                                continue;
354                        }
[a3f8d58]355                        sum += iq_in[i] * (*weights)[q_i*nbins+i];
356                        counts += (*weights)[q_i*nbins+i];
357                }
358
359                // Normalize counts
[cd2ced80]360                iq_out[q_i] = (counts>0.0) ? sum/counts : 0.0;
[a3f8d58]361        }
362}
Note: See TracBrowser for help on using the repository browser.