source: sasview/sansmodels/src/c_models/coreshellcylinder.cpp @ 011e0e4

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 011e0e4 was 011e0e4, checked in by Mathieu Doucet <doucetm@…>, 12 years ago

core-shell + ellipsoid refactor

  • Property mode set to 100644
File size: 10.9 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.