source: sasview/sansmodels/src/c_models/coreshellcylinder.cpp @ ccb7363

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 ccb7363 was 82c11d3, checked in by Mathieu Doucet <doucetm@…>, 13 years ago

refactored bunch of models

  • Property mode set to 100644
File size: 11.4 KB
Line 
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#include "core_shell_cylinder.h"
27
28extern "C" {
29#include "libCylinder.h"
30#include "libStructureFactor.h"
31}
32
33typedef struct {
34  double scale;
35  double radius;
36  double thickness;
37  double length;
38  double core_sld;
39  double shell_sld;
40  double solvent_sld;
41  double background;
42  double axis_theta;
43  double axis_phi;
44} CoreShellCylinderParameters;
45
46
47/**
48 * Function to evaluate 2D scattering function
49 * @param pars: parameters of the core-shell cylinder
50 * @param q: q-value
51 * @param q_x: q_x / q
52 * @param q_y: q_y / q
53 * @return: function value
54 */
55static double core_shell_cylinder_analytical_2D_scaled(CoreShellCylinderParameters *pars, double q, double q_x, double q_y) {
56  double cyl_x, cyl_y, cyl_z;
57  double q_z;
58  double alpha, vol, cos_val;
59  double answer;
60  //convert angle degree to radian
61  double pi = 4.0*atan(1.0);
62  double theta = pars->axis_theta * pi/180.0;
63  double phi = pars->axis_phi * pi/180.0;
64
65  // Cylinder orientation
66  cyl_x = sin(theta) * cos(phi);
67  cyl_y = sin(theta) * sin(phi);
68  cyl_z = cos(theta);
69
70  // q vector
71  q_z = 0;
72
73  // Compute the angle btw vector q and the
74  // axis of the cylinder
75  cos_val = cyl_x*q_x + cyl_y*q_y + cyl_z*q_z;
76
77  // The following test should always pass
78  if (fabs(cos_val)>1.0) {
79    printf("core_shell_cylinder_analytical_2D: Unexpected error: cos(alpha)=%g\n", cos_val);
80    return 0;
81  }
82
83  alpha = acos( cos_val );
84
85  // Call the IGOR library function to get the kernel
86  answer = CoreShellCylKernel(q, pars->radius, pars->thickness,
87      pars->core_sld,pars->shell_sld,
88      pars->solvent_sld, pars->length/2.0, alpha) / fabs(sin(alpha));
89
90  //normalize by cylinder volume
91  vol=pi*(pars->radius+pars->thickness)
92          *(pars->radius+pars->thickness)
93          *(pars->length+2.0*pars->thickness);
94  answer /= vol;
95
96  //convert to [cm-1]
97  answer *= 1.0e8;
98
99  //Scale
100  answer *= pars->scale;
101
102  // add in the background
103  answer += pars->background;
104
105  return answer;
106}
107
108/**
109 * Function to evaluate 2D scattering function
110 * @param pars: parameters of the core-shell cylinder
111 * @param q: q-value
112 * @return: function value
113 */
114static double core_shell_cylinder_analytical_2DXY(CoreShellCylinderParameters *pars, double qx, double qy) {
115  double q;
116  q = sqrt(qx*qx+qy*qy);
117  return core_shell_cylinder_analytical_2D_scaled(pars, q, qx/q, qy/q);
118}
119
120
121CoreShellCylinderModel :: CoreShellCylinderModel() {
122  scale      = Parameter(1.0);
123  radius     = Parameter(20.0, true);
124  radius.set_min(0.0);
125  thickness  = Parameter(10.0, true);
126  thickness.set_min(0.0);
127  length     = Parameter(400.0, true);
128  length.set_min(0.0);
129  core_sld   = Parameter(1.e-6);
130  shell_sld  = Parameter(4.e-6);
131  solvent_sld= Parameter(1.e-6);
132  background = Parameter(0.0);
133  axis_theta = Parameter(90.0, true);
134  axis_phi   = Parameter(0.0, true);
135}
136
137/**
138 * Function to evaluate 1D scattering function
139 * The NIST IGOR library is used for the actual calculation.
140 * @param q: q-value
141 * @return: function value
142 */
143double CoreShellCylinderModel :: operator()(double q) {
144  double dp[8];
145
146  dp[0] = scale();
147  dp[1] = radius();
148  dp[2] = thickness();
149  dp[3] = length();
150  dp[4] = core_sld();
151  dp[5] = shell_sld();
152  dp[6] = solvent_sld();
153  dp[7] = 0.0;
154
155  // Get the dispersion points for the radius
156  vector<WeightPoint> weights_rad;
157  radius.get_weights(weights_rad);
158
159  // Get the dispersion points for the thickness
160  vector<WeightPoint> weights_thick;
161  thickness.get_weights(weights_thick);
162
163  // Get the dispersion points for the length
164  vector<WeightPoint> weights_len;
165  length.get_weights(weights_len);
166
167  // Perform the computation, with all weight points
168  double sum = 0.0;
169  double norm = 0.0;
170  double vol = 0.0;
171
172  // Loop over radius weight points
173  for(size_t i=0; i<weights_rad.size(); i++) {
174    dp[1] = weights_rad[i].value;
175
176    // Loop over length weight points
177    for(size_t j=0; j<weights_len.size(); j++) {
178      dp[3] = weights_len[j].value;
179
180      // Loop over thickness weight points
181      for(size_t k=0; k<weights_thick.size(); k++) {
182        dp[2] = weights_thick[k].value;
183        //Un-normalize by volume
184        sum += weights_rad[i].weight
185            * weights_len[j].weight
186            * weights_thick[k].weight
187            * CoreShellCylinder(dp, q)
188        * pow(weights_rad[i].value+weights_thick[k].value,2)
189        *(weights_len[j].value+2.0*weights_thick[k].value);
190        //Find average volume
191        vol += weights_rad[i].weight
192            * weights_len[j].weight
193            * weights_thick[k].weight
194            * pow(weights_rad[i].value+weights_thick[k].value,2)
195        *(weights_len[j].value+2.0*weights_thick[k].value);
196        norm += weights_rad[i].weight
197            * weights_len[j].weight
198            * weights_thick[k].weight;
199      }
200    }
201  }
202
203  if (vol != 0.0 && norm != 0.0) {
204    //Re-normalize by avg volume
205    sum = sum/(vol/norm);}
206
207  return sum/norm + background();
208}
209
210/**
211 * Function to evaluate 2D scattering function
212 * @param q_x: value of Q along x
213 * @param q_y: value of Q along y
214 * @return: function value
215 */
216double CoreShellCylinderModel :: operator()(double qx, double qy) {
217  CoreShellCylinderParameters dp;
218  // Fill parameter array
219  dp.scale      = scale();
220  dp.radius     = radius();
221  dp.thickness  = thickness();
222  dp.length     = length();
223  dp.core_sld   = core_sld();
224  dp.shell_sld  = shell_sld();
225  dp.solvent_sld= solvent_sld();
226  dp.background = 0.0;
227  dp.axis_theta = axis_theta();
228  dp.axis_phi   = axis_phi();
229
230  // Get the dispersion points for the radius
231  vector<WeightPoint> weights_rad;
232  radius.get_weights(weights_rad);
233
234  // Get the dispersion points for the thickness
235  vector<WeightPoint> weights_thick;
236  thickness.get_weights(weights_thick);
237
238  // Get the dispersion points for the length
239  vector<WeightPoint> weights_len;
240  length.get_weights(weights_len);
241
242  // Get angular averaging for theta
243  vector<WeightPoint> weights_theta;
244  axis_theta.get_weights(weights_theta);
245
246  // Get angular averaging for phi
247  vector<WeightPoint> weights_phi;
248  axis_phi.get_weights(weights_phi);
249
250  // Perform the computation, with all weight points
251  double sum = 0.0;
252  double norm = 0.0;
253  double norm_vol = 0.0;
254  double vol = 0.0;
255  double pi = 4.0*atan(1.0);
256  // Loop over radius weight points
257  for(size_t i=0; i<weights_rad.size(); i++) {
258    dp.radius = weights_rad[i].value;
259
260
261    // Loop over length weight points
262    for(size_t j=0; j<weights_len.size(); j++) {
263      dp.length = weights_len[j].value;
264
265      // Loop over thickness weight points
266      for(size_t m=0; m<weights_thick.size(); m++) {
267        dp.thickness = weights_thick[m].value;
268
269        // Average over theta distribution
270        for(size_t k=0; k<weights_theta.size(); k++) {
271          dp.axis_theta = weights_theta[k].value;
272
273          // Average over phi distribution
274          for(size_t l=0; l<weights_phi.size(); l++) {
275            dp.axis_phi = weights_phi[l].value;
276            //Un-normalize by volume
277            double _ptvalue = weights_rad[i].weight
278                * weights_len[j].weight
279                * weights_thick[m].weight
280                * weights_theta[k].weight
281                * weights_phi[l].weight
282                * core_shell_cylinder_analytical_2DXY(&dp, qx, qy)
283            * pow(weights_rad[i].value+weights_thick[m].value,2)
284            *(weights_len[j].value+2.0*weights_thick[m].value);
285
286            if (weights_theta.size()>1) {
287              _ptvalue *= fabs(sin(weights_theta[k].value*pi/180.0));
288            }
289            sum += _ptvalue;
290
291            //Find average volume
292            vol += weights_rad[i].weight
293                * weights_len[j].weight
294                * weights_thick[m].weight
295                * pow(weights_rad[i].value+weights_thick[m].value,2)
296            *(weights_len[j].value+2.0*weights_thick[m].value);
297            //Find norm for volume
298            norm_vol += weights_rad[i].weight
299                * weights_len[j].weight
300                * weights_thick[m].weight;
301
302            norm += weights_rad[i].weight
303                * weights_len[j].weight
304                * weights_thick[m].weight
305                * weights_theta[k].weight
306                * weights_phi[l].weight;
307
308          }
309        }
310      }
311    }
312  }
313  // Averaging in theta needs an extra normalization
314  // factor to account for the sin(theta) term in the
315  // integration (see documentation).
316  if (weights_theta.size()>1) norm = norm / asin(1.0);
317
318  if (vol != 0.0 && norm_vol != 0.0) {
319    //Re-normalize by avg volume
320    sum = sum/(vol/norm_vol);}
321
322  return sum/norm + background();
323}
324
325/**
326 * Function to evaluate 2D scattering function
327 * @param pars: parameters of the cylinder
328 * @param q: q-value
329 * @param phi: angle phi
330 * @return: function value
331 */
332double CoreShellCylinderModel :: evaluate_rphi(double q, double phi) {
333  double qx = q*cos(phi);
334  double qy = q*sin(phi);
335  return (*this).operator()(qx, qy);
336}
337/**
338 * Function to calculate effective radius
339 * @return: effective radius value
340 */
341double CoreShellCylinderModel :: calculate_ER() {
342  CoreShellCylinderParameters dp;
343
344  dp.radius     = radius();
345  dp.thickness  = thickness();
346  dp.length     = length();
347  double rad_out = 0.0;
348
349  // Perform the computation, with all weight points
350  double sum = 0.0;
351  double norm = 0.0;
352
353  // Get the dispersion points for the length
354  vector<WeightPoint> weights_length;
355  length.get_weights(weights_length);
356
357  // Get the dispersion points for the thickness
358  vector<WeightPoint> weights_thickness;
359  thickness.get_weights(weights_thickness);
360
361  // Get the dispersion points for the radius
362  vector<WeightPoint> weights_radius ;
363  radius.get_weights(weights_radius);
364
365  // Loop over major shell weight points
366  for(int i=0; i< (int)weights_length.size(); i++) {
367    dp.length = weights_length[i].value;
368    for(int j=0; j< (int)weights_thickness.size(); j++) {
369      dp.thickness = weights_thickness[j].value;
370      for(int k=0; k< (int)weights_radius.size(); k++) {
371        dp.radius = weights_radius[k].value;
372        //Note: output of "DiamCyl( )" is DIAMETER.
373        sum +=weights_length[i].weight * weights_thickness[j].weight
374            * weights_radius[k].weight*DiamCyl(dp.length+2.0*dp.thickness,dp.radius+dp.thickness)/2.0;
375        norm += weights_length[i].weight* weights_thickness[j].weight* weights_radius[k].weight;
376      }
377    }
378  }
379  if (norm != 0){
380    //return the averaged value
381    rad_out =  sum/norm;}
382  else{
383    //return normal value
384    //Note: output of "DiamCyl()" is DIAMETER.
385    rad_out = DiamCyl(dp.length+2.0*dp.thickness,dp.radius+dp.thickness)/2.0;}
386
387  return rad_out;
388}
Note: See TracBrowser for help on using the repository browser.