source: sasview/sansmodels/src/c_models/capcyl.cpp @ dcb0834

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

refactored capped cylinder

  • Property mode set to 100644
File size: 11.2 KB
RevLine 
[339ce67]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 2008, University of Tennessee
13 */
14
15/**
16 * Scattering model classes
17 * The classes use the IGOR library found in
18 *   sansmodels/src/libigor
19 *
20 */
21
22#include <math.h>
23#include "parameters.hh"
24#include <stdio.h>
25using namespace std;
26
27extern "C" {
[0c2389e]28  #include "GaussWeights.h"
[339ce67]29        #include "libCylinder.h"
30}
[0c2389e]31#include "capcyl.h"
32
33// Convenience parameter structure
34typedef struct {
35    double scale;
36    double rad_cyl;
37    double len_cyl;
38    double rad_cap;
39    double sld_capcyl;
40    double sld_solv;
41    double background;
42    double theta;
43    double phi;
44} CapCylParameters;
[339ce67]45
46CappedCylinderModel :: CappedCylinderModel() {
47        scale      = Parameter(1.0);
48        rad_cyl         = Parameter(20.0);
49        rad_cyl.set_min(0.0);
50        len_cyl     = Parameter(400.0, true);
51        len_cyl.set_min(0.0);
52        rad_cap = Parameter(40.0);
53        rad_cap.set_min(0.0);
54        sld_capcyl  = Parameter(1.0e-6);
55        sld_solv   = Parameter(6.3e-6);
56        background = Parameter(0.0);
57        theta  = Parameter(0.0, true);
58        phi    = Parameter(0.0, true);
59}
60
[0c2389e]61static double capcyl2d_kernel(double dp[], double q, double alpha) {
62  int j;
63  double Pi;
64  double scale,contr,bkg,sldc,slds;
65  double len,rad,hDist,endRad;
66  int nordj=76;
67  double zi=alpha,yyy,answer;     //running tally of integration
68  double summj,vaj,vbj,zij;     //for the inner integration
69  double arg1,arg2,inner,be;
70
71
72  scale = dp[0];
73  rad = dp[1];
74  len = dp[2];
75  endRad = dp[3];
76  sldc = dp[4];
77  slds = dp[5];
78  bkg = dp[6];
79
80  hDist = -1.0*sqrt(fabs(endRad*endRad-rad*rad));   //by definition for this model
81
82  contr = sldc-slds;
83
84  Pi = 4.0*atan(1.0);
85  vaj = -1.0*hDist/endRad;
86  vbj = 1.0;    //endpoints of inner integral
87
88  summj=0.0;
89
90  for(j=0;j<nordj;j++) {
91    //20 gauss points for the inner integral
92    zij = ( Gauss76Z[j]*(vbj-vaj) + vaj + vbj )/2.0;    //the "t" dummy
93    yyy = Gauss76Wt[j] * ConvLens_kernel(dp,q,zij,zi);    //uses the same Kernel as the Dumbbell, here L>0
94    summj += yyy;
95  }
96  //now calculate the value of the inner integral
97  inner = (vbj-vaj)/2.0*summj;
98  inner *= 4.0*Pi*endRad*endRad*endRad;
99
100  //now calculate outer integrand
101  arg1 = q*len/2.0*cos(zi);
102  arg2 = q*rad*sin(zi);
103  yyy = inner;
104
105  if(arg2 == 0) {
106    be = 0.5;
107  } else {
108    be = NR_BessJ1(arg2)/arg2;
109  }
110
111  if(arg1 == 0.0) {   //limiting value of sinc(0) is 1; sinc is not defined in math.h
112    yyy += Pi*rad*rad*len*2.0*be;
113  } else {
114    yyy += Pi*rad*rad*len*sin(arg1)/arg1*2.0*be;
115  }
116  yyy *= yyy;   //sin(zi);
117  answer = yyy;
118
119
120  answer /= Pi*rad*rad*len + 2.0*Pi*(2.0*endRad*endRad*endRad/3.0+endRad*endRad*hDist-hDist*hDist*hDist/3.0);   //divide by volume
121  answer *= 1.0e8;    //convert to cm^-1
122  answer *= contr*contr;
123  answer *= scale;
124  answer += bkg;
125
126  return answer;
127}
128
129/**
130 * Function to evaluate 2D scattering function
131 * @param pars: parameters of the BarBell
132 * @param q: q-value
133 * @param q_x: q_x / q
134 * @param q_y: q_y / q
135 * @return: function value
136 */
137static double capcyl_analytical_2D_scaled(CapCylParameters *pars, double q, double q_x, double q_y) {
138  double cyl_x, cyl_y, cyl_z;
139  double q_z;
140  double alpha, cos_val;
141  double answer;
142  double dp[7];
143  //convert angle degree to radian
144  double pi = 4.0*atan(1.0);
145  double theta = pars->theta * pi/180.0;
146  double phi = pars->phi * pi/180.0;
147
148  dp[0] = pars->scale;
149  dp[1] = pars->rad_cyl;
150  dp[2] = pars->len_cyl;
151  dp[3] = pars->rad_cap;
152  dp[4] = pars->sld_capcyl;
153  dp[5] = pars->sld_solv;
154  dp[6] = pars->background;
155
156
157  //double Pi = 4.0*atan(1.0);
158    // Cylinder orientation
159    cyl_x = sin(theta) * cos(phi);
160    cyl_y = sin(theta) * sin(phi);
161    cyl_z = cos(theta);
162
163    // q vector
164    q_z = 0;
165
166    // Compute the angle btw vector q and the
167    // axis of the cylinder
168    cos_val = cyl_x*q_x + cyl_y*q_y + cyl_z*q_z;
169
170    // The following test should always pass
171    if (fabs(cos_val)>1.0) {
172      printf("cyl_ana_2D: Unexpected error: cos(alpha)>1\n");
173      return 0;
174    }
175
176    // Note: cos(alpha) = 0 and 1 will get an
177    // undefined value from CylKernel
178  alpha = acos( cos_val );
179
180  // Call the IGOR library function to get the kernel
181  answer = capcyl2d_kernel(dp, q, alpha)/sin(alpha);
182
183
184  return answer;
185
186}
187
188/**
189 * Function to evaluate 2D scattering function
190 * @param pars: parameters of the BarBell
191 * @param q: q-value
192 * @return: function value
193 */
194static double capcyl_analytical_2DXY(CapCylParameters *pars, double qx, double qy){
195  double q;
196  q = sqrt(qx*qx+qy*qy);
197  return capcyl_analytical_2D_scaled(pars, q, qx/q, qy/q);
198}
199
[339ce67]200/**
201 * Function to evaluate 1D scattering function
202 * The NIST IGOR library is used for the actual calculation.
203 * @param q: q-value
204 * @return: function value
205 */
206double CappedCylinderModel :: operator()(double q) {
207        double dp[7];
208
209        // Fill parameter array for IGOR library
210        // Add the background after averaging
211        dp[0] = scale();
212        dp[1] = rad_cyl();
213        dp[2] = len_cyl();
214        dp[3] = rad_cap();
215        dp[4] = sld_capcyl();
216        dp[5] = sld_solv();
217        dp[6] = 0.0;
218
219        // Get the dispersion points for the rad_cyl
220        vector<WeightPoint> weights_rad_cyl;
221        rad_cyl.get_weights(weights_rad_cyl);
222        // Get the dispersion points for the len_cyl
223        vector<WeightPoint> weights_len_cyl;
224        len_cyl.get_weights(weights_len_cyl);
225        // Get the dispersion points for the rad_cap
226        vector<WeightPoint> weights_rad_cap;
227        rad_cap.get_weights(weights_rad_cap);
228
229        // Perform the computation, with all weight points
230        double sum = 0.0;
231        double norm = 0.0;
232        double vol = 0.0;
233        double pi,hDist,result;
234        double vol_i = 0.0;
235        pi = 4.0*atan(1.0);
236        // Loop over radius weight points
[34c2649]237        for(size_t i=0; i<weights_rad_cyl.size(); i++) {
[339ce67]238                dp[1] = weights_rad_cyl[i].value;
[34c2649]239                for(size_t j=0; j<weights_len_cyl.size(); j++) {
[339ce67]240                        dp[2] = weights_len_cyl[j].value;
[34c2649]241                        for(size_t k=0; k<weights_rad_cap.size(); k++) {
[339ce67]242                                dp[3] = weights_rad_cap[k].value;
243
244                                //Un-normalize SphereForm by volume
245                                hDist = -1.0*sqrt(fabs(dp[3]*dp[3]-dp[1]*dp[1]));
246                                vol_i = pi*dp[1]*dp[1]*dp[2]+2.0*pi/3.0*((dp[3]-hDist)*(dp[3]-hDist)*
247                                                                (2.0*(dp[3]+hDist)));
248                                result =  CappedCylinder(dp, q) * vol_i;
249                                // This FIXES a singualrity the kernel in libigor.
250                                if ( result == INFINITY || result == NAN){
251                                        result = 0.0;
252                                }
253                                sum += weights_rad_cyl[i].weight*weights_len_cyl[j].weight*weights_rad_cap[k].weight
254                                        * result;
255                                //Find average volume
256                                vol += weights_rad_cyl[i].weight*weights_len_cyl[j].weight*weights_rad_cap[k].weight
257                                        * vol_i;
258
259                                norm += weights_rad_cyl[i].weight*weights_len_cyl[j].weight*weights_rad_cap[k].weight;
260                        }
261                }
262        }
263
264        if (vol != 0.0 && norm != 0.0) {
265                //Re-normalize by avg volume
266                sum = sum/(vol/norm);}
267        return sum/norm + background();
268}
269
270/**
271 * Function to evaluate 2D scattering function
272 * @param q_x: value of Q along x
273 * @param q_y: value of Q along y
274 * @return: function value
275 */
276double CappedCylinderModel :: operator()(double qx, double qy) {
277        CapCylParameters dp;
278
279        dp.scale = scale();
280        dp.rad_cyl = rad_cyl();
281        dp.len_cyl = len_cyl();
282        dp.rad_cap = rad_cap();
283        dp.sld_capcyl = sld_capcyl();
284        dp.sld_solv = sld_solv();
285        dp.background = 0.0;
286        dp.theta  = theta();
287        dp.phi    = phi();
288
289        // Get the dispersion points for the rad_bar
290        vector<WeightPoint> weights_rad_cyl;
291        rad_cyl.get_weights(weights_rad_cyl);
292
293        // Get the dispersion points for the len_bar
294        vector<WeightPoint> weights_len_cyl;
295        len_cyl.get_weights(weights_len_cyl);
296
297        // Get the dispersion points for the rad_bell
298        vector<WeightPoint> weights_rad_cap;
299        rad_cap.get_weights(weights_rad_cap);
300
301        // Get angular averaging for theta
302        vector<WeightPoint> weights_theta;
303        theta.get_weights(weights_theta);
304
305        // Get angular averaging for phi
306        vector<WeightPoint> weights_phi;
307        phi.get_weights(weights_phi);
308
309
310        // Perform the computation, with all weight points
311        double sum = 0.0;
312        double norm = 0.0;
313        double norm_vol = 0.0;
314        double vol = 0.0;
[34c2649]315        double pi,hDist;
[339ce67]316        double vol_i = 0.0;
317        pi = 4.0*atan(1.0);
318
319        // Loop over radius weight points
[34c2649]320        for(size_t i=0; i<weights_rad_cyl.size(); i++) {
[339ce67]321                dp.rad_cyl = weights_rad_cyl[i].value;
[34c2649]322                for(size_t j=0; j<weights_len_cyl.size(); j++) {
[339ce67]323                        dp.len_cyl = weights_len_cyl[j].value;
[34c2649]324                        for(size_t k=0; k<weights_rad_cap.size(); k++) {
[339ce67]325                                dp.rad_cap = weights_rad_cap[k].value;
326                                // Average over theta distribution
[34c2649]327                                for(size_t l=0; l< weights_theta.size(); l++) {
[339ce67]328                                        dp.theta = weights_theta[l].value;
329                                        // Average over phi distribution
[34c2649]330                                        for(size_t m=0; m< weights_phi.size(); m++) {
[339ce67]331                                                dp.phi = weights_phi[m].value;
332                                                //Un-normalize Form by volume
333                                                hDist = -1.0*sqrt(fabs(dp.rad_cap*dp.rad_cap-dp.rad_cyl*dp.rad_cyl));
334                                                vol_i = pi*dp.rad_cyl*dp.rad_cyl*dp.len_cyl+2.0*pi/3.0*((dp.rad_cap-hDist)*(dp.rad_cap-hDist)*
335                                                                                (2*dp.rad_cap+hDist));
336
337                                                double _ptvalue = weights_rad_cyl[i].weight
338                                                                                        * weights_len_cyl[j].weight
339                                                                                        * weights_rad_cap[k].weight
340                                                                                        * weights_theta[l].weight
341                                                                                        * weights_phi[m].weight
342                                                                                        * vol_i
343                                                                                        * capcyl_analytical_2DXY(&dp, qx, qy);
344                                                                                        //* pow(weights_rad[i].value,3.0);
345                                                // Consider when there is infinte or nan.
346                                                if ( _ptvalue == INFINITY || _ptvalue == NAN){
347                                                        _ptvalue = 0.0;
348                                                }
349                                                if (weights_theta.size()>1) {
[4628e31]350                                                        _ptvalue *= fabs(sin(weights_theta[l].value*pi/180.0));
[339ce67]351                                                }
352                                                sum += _ptvalue;
353                                                // This model dose not need the volume of spheres correction!!!
354                                                //Find average volume
355                                                vol += weights_rad_cyl[i].weight
356                                                                * weights_len_cyl[j].weight
357                                                                * weights_rad_cap[k].weight
358                                                                * vol_i;
359                                                //Find norm for volume
360                                                norm_vol += weights_rad_cyl[i].weight
361                                                                * weights_len_cyl[j].weight
362                                                                * weights_rad_cap[k].weight;
363
364                                                norm += weights_rad_cyl[i].weight
365                                                                * weights_len_cyl[j].weight
366                                                                * weights_rad_cap[k].weight
367                                                                * weights_theta[l].weight
368                                                                * weights_phi[m].weight;
369                                        }
370                                }
371                        }
372                }
373        }
374        // Averaging in theta needs an extra normalization
375        // factor to account for the sin(theta) term in the
376        // integration (see documentation).
377        if (weights_theta.size()>1) norm = norm / asin(1.0);
378
379        if (vol != 0.0 && norm_vol != 0.0) {
380                //Re-normalize by avg volume
381                sum = sum/(vol/norm_vol);}
382
383        return sum/norm + background();
384}
385
386/**
387 * Function to evaluate 2D scattering function
388 * @param pars: parameters of the SCCrystal
389 * @param q: q-value
390 * @param phi: angle phi
391 * @return: function value
392 */
393double CappedCylinderModel :: evaluate_rphi(double q, double phi) {
394        return (*this).operator()(q);
395}
396
397/**
398 * Function to calculate effective radius
399 * @return: effective radius value
400 */
401double CappedCylinderModel :: calculate_ER() {
402        //NOT implemented yet!!!
[34c2649]403        return 0.0;
[339ce67]404}
Note: See TracBrowser for help on using the repository browser.