source: sasview/sansmodels/src/c_models/cylinder.cpp @ 476977b

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 476977b was 318b5bbb, checked in by Jae Cho <jhjcho@…>, 12 years ago

Added polarization and magnetic stuffs

  • Property mode set to 100644
File size: 12.1 KB
RevLine 
[fca6936]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" {
28        #include "libCylinder.h"
[5eb9154]29        #include "libStructureFactor.h"
[318b5bbb]30        #include "libmultifunc/libfunc.h"
[fca6936]31}
[0c2389e]32#include "cylinder.h"
[fca6936]33
[dbddbf5]34// Convenience parameter structure
35typedef struct {
36    double scale;
37    double radius;
38    double length;
39    double sldCyl;
40    double sldSolv;
41    double background;
42    double cyl_theta;
43    double cyl_phi;
[318b5bbb]44    double M0_sld_cyl;
45    double M_theta_cyl;
46    double M_phi_cyl;
47    double M0_sld_solv;
48    double M_theta_solv;
49    double M_phi_solv;
50    double Up_frac_i;
51        double Up_frac_f;
52        double Up_theta;
[dbddbf5]53} CylinderParameters;
54
[af03ddd]55CylinderModel :: CylinderModel() {
[fca6936]56        scale      = Parameter(1.0);
57        radius     = Parameter(20.0, true);
58        radius.set_min(0.0);
59        length     = Parameter(400.0, true);
60        length.set_min(0.0);
[f10063e]61        sldCyl   = Parameter(4.e-6);
62        sldSolv   = Parameter(1.e-6);
[fca6936]63        background = Parameter(0.0);
64        cyl_theta  = Parameter(0.0, true);
65        cyl_phi    = Parameter(0.0, true);
[318b5bbb]66        M0_sld_cyl = Parameter(0.0e-6);
67        M_theta_cyl = Parameter(0.0);
68        M_phi_cyl = Parameter(0.0); 
69        M0_sld_solv = Parameter(0.0e-6);
70        M_theta_solv = Parameter(0.0);
71        M_phi_solv = Parameter(0.0); 
72        Up_frac_i = Parameter(0.5); 
73        Up_frac_f = Parameter(0.5);
74        Up_theta = Parameter(0.0);
[fca6936]75}
76
77/**
78 * Function to evaluate 1D scattering function
79 * The NIST IGOR library is used for the actual calculation.
80 * @param q: q-value
81 * @return: function value
82 */
[af03ddd]83double CylinderModel :: operator()(double q) {
[f10063e]84        double dp[6];
[fca6936]85
86        // Fill parameter array for IGOR library
87        // Add the background after averaging
88        dp[0] = scale();
89        dp[1] = radius();
90        dp[2] = length();
[f10063e]91        dp[3] = sldCyl();
92        dp[4] = sldSolv();
93        dp[5] = 0.0;
[fca6936]94
95        // Get the dispersion points for the radius
96        vector<WeightPoint> weights_rad;
97        radius.get_weights(weights_rad);
98
99        // Get the dispersion points for the length
100        vector<WeightPoint> weights_len;
101        length.get_weights(weights_len);
102
103        // Perform the computation, with all weight points
104        double sum = 0.0;
105        double norm = 0.0;
[c451be9]106        double vol = 0.0;
[fca6936]107
108        // Loop over radius weight points
[34c2649]109        for(size_t i=0; i<weights_rad.size(); i++) {
[fca6936]110                dp[1] = weights_rad[i].value;
111
112                // Loop over length weight points
[34c2649]113                for(size_t j=0; j<weights_len.size(); j++) {
[fca6936]114                        dp[2] = weights_len[j].value;
[c451be9]115                        //Un-normalize by volume
[fca6936]116                        sum += weights_rad[i].weight
[c451be9]117                                * weights_len[j].weight * CylinderForm(dp, q)
118                                *pow(weights_rad[i].value,2)*weights_len[j].value;
119
120                        //Find average volume
121                        vol += weights_rad[i].weight
122                                * weights_len[j].weight *pow(weights_rad[i].value,2)*weights_len[j].value;
[fca6936]123                        norm += weights_rad[i].weight
124                                * weights_len[j].weight;
125                }
126        }
[c451be9]127        if (vol != 0.0 && norm != 0.0) {
128                //Re-normalize by avg volume
129                sum = sum/(vol/norm);}
130
[fca6936]131        return sum/norm + background();
132}
133
134/**
135 * Function to evaluate 2D scattering function
[dbddbf5]136 * @param pars: parameters of the cylinder
137 * @param q: q-value
138 * @param q_x: q_x / q
139 * @param q_y: q_y / q
140 * @return: function value
141 */
142static double cylinder_analytical_2D_scaled(CylinderParameters *pars, double q, double q_x, double q_y) {
[318b5bbb]143    double cyl_x, cyl_y;//, cyl_z;
144    //double q_z;
145    double alpha, vol, cos_val;
146    double answer = 0.0;
147    double form = 0.0;
148    //convert angle degree to radian
149    double pi = 4.0*atan(1.0);
150    double theta = pars->cyl_theta * pi/180.0;
151    double phi = pars->cyl_phi * pi/180.0;
152    double sld_solv = pars->sldSolv;
153    double sld_cyl = pars->sldCyl;
154    double m_max = pars->M0_sld_cyl;
155    double m_max_solv = pars->M0_sld_solv;
156    double contrast = 0.0;
[dbddbf5]157
158    // Cylinder orientation
[318b5bbb]159        cyl_x = cos(theta) * cos(phi);
160    cyl_y = sin(theta);
161    //cyl_z = -cos(theta) * sin(phi);
[dbddbf5]162    // q vector
[318b5bbb]163    //q_z = 0.0;
[dbddbf5]164
165    // Compute the angle btw vector q and the
166    // axis of the cylinder
[318b5bbb]167    cos_val = cyl_x*q_x + cyl_y*q_y;// + cyl_z*q_z;
[dbddbf5]168
169    // The following test should always pass
170    if (fabs(cos_val)>1.0) {
[d49c956]171      printf("cyl_ana_2D: Unexpected error: |cos(alpha)=%g|>1\n", cos_val);
172      printf("cyl_ana_2D: at theta=%g and phi=%g.", theta, phi);
173      return 1.0;
[dbddbf5]174    }
175
176    // Note: cos(alpha) = 0 and 1 will get an
177    // undefined value from CylKernel
178  alpha = acos( cos_val );
[20efe7b]179  if (alpha == 0.0){
180        alpha = 1.0e-26;
181        }
[dbddbf5]182  // Call the IGOR library function to get the kernel
[318b5bbb]183  //answer = CylKernel(q, pars->radius, pars->length/2.0, alpha) / sin(alpha);
[dbddbf5]184
[318b5bbb]185    // Call the IGOR library function to get the kernel
186    form = CylKernel(q, pars->radius, pars->length/2.0, alpha) / sin(alpha);
[dbddbf5]187
[318b5bbb]188        if (m_max < 1.0e-32 && m_max_solv < 1.0e-32){
189                // Multiply by contrast^2
190                contrast = (pars->sldCyl - pars->sldSolv);
191                answer = contrast * contrast * form;
192        }
193        else{
194                double qx = q_x;
195                double qy = q_y;
196                double s_theta = pars->Up_theta;
197                double m_phi = pars->M_phi_cyl;
198                double m_theta = pars->M_theta_cyl;
199                double m_phi_solv = pars->M_phi_solv;
200                double m_theta_solv = pars->M_theta_solv;
201                double in_spin = pars->Up_frac_i;
202                double out_spin = pars->Up_frac_f;
203                polar_sld p_sld;
204                polar_sld p_sld_solv;
205                p_sld = cal_msld(1, qx, qy, sld_cyl, m_max, m_theta, m_phi, 
206                                                in_spin, out_spin, s_theta);
207                p_sld_solv = cal_msld(1, qx, qy, sld_solv, m_max_solv, m_theta_solv, m_phi_solv, 
208                                                in_spin, out_spin, s_theta);
209                //up_up
210                if (in_spin > 0.0 && out_spin > 0.0){                   
211                        answer += ((p_sld.uu- p_sld_solv.uu) * (p_sld.uu- p_sld_solv.uu) * form);
212                        }
213                //down_down
214                if (in_spin < 1.0 && out_spin < 1.0){
215                        answer += ((p_sld.dd - p_sld_solv.dd) * (p_sld.dd - p_sld_solv.dd) * form);
216                        }
217                //up_down
218                if (in_spin > 0.0 && out_spin < 1.0){
219                        answer += ((p_sld.re_ud - p_sld_solv.re_ud) * (p_sld.re_ud - p_sld_solv.re_ud) * form);
220                        answer += ((p_sld.im_ud - p_sld_solv.im_ud) * (p_sld.im_ud - p_sld_solv.im_ud) * form);
221                        }
222                //down_up       
223                if (in_spin < 1.0 && out_spin > 0.0){
224                        answer += ((p_sld.re_du - p_sld_solv.re_du) * (p_sld.re_du - p_sld_solv.re_du) * form);
225                        answer += ((p_sld.im_du - p_sld_solv.im_du) * (p_sld.im_du - p_sld_solv.im_du) * form);
226                        }
227        }
228
229    //normalize by cylinder volume
230    //NOTE that for this (Fournet) definition of the integral, one must MULTIPLY by Vcyl
[dbddbf5]231    vol = acos(-1.0) * pars->radius * pars->radius * pars->length;
[318b5bbb]232    answer *= vol;
[dbddbf5]233
[318b5bbb]234    //convert to [cm-1]
235    answer *= 1.0e8;
[dbddbf5]236
[318b5bbb]237    //Scale
238    answer *= pars->scale;
[dbddbf5]239
[318b5bbb]240    // add in the background
241    answer += pars->background;
[dbddbf5]242
[318b5bbb]243    return answer;
[dbddbf5]244}
245
246/**
247 * Function to evaluate 2D scattering function
248 * @param pars: parameters of the cylinder
249 * @param q: q-value
250 * @return: function value
251 */
252static double cylinder_analytical_2DXY(CylinderParameters *pars, double qx, double qy) {
253  double q;
254  q = sqrt(qx*qx+qy*qy);
255  return cylinder_analytical_2D_scaled(pars, q, qx/q, qy/q);
256}
257
258/**
259 * Function to evaluate 2D scattering function
[fca6936]260 * @param q_x: value of Q along x
261 * @param q_y: value of Q along y
262 * @return: function value
263 */
[af03ddd]264double CylinderModel :: operator()(double qx, double qy) {
[fca6936]265        CylinderParameters dp;
266        // Fill parameter array
267        dp.scale      = scale();
268        dp.radius     = radius();
269        dp.length     = length();
[f10063e]270        dp.sldCyl   = sldCyl();
271        dp.sldSolv   = sldSolv();
[fca6936]272        dp.background = 0.0;
273        dp.cyl_theta  = cyl_theta();
274        dp.cyl_phi    = cyl_phi();
[318b5bbb]275        dp.Up_theta =  Up_theta();
276        dp.M_phi_cyl =  M_phi_cyl();
277        dp.M_theta_cyl =  M_theta_cyl();
278        dp.M0_sld_cyl =  M0_sld_cyl();
279        dp.M_phi_solv =  M_phi_solv();
280        dp.M_theta_solv =  M_theta_solv();
281        dp.M0_sld_solv =  M0_sld_solv();
282        dp.Up_frac_i =  Up_frac_i();
283        dp.Up_frac_f =  Up_frac_f();
284       
[fca6936]285        // Get the dispersion points for the radius
286        vector<WeightPoint> weights_rad;
287        radius.get_weights(weights_rad);
288
289        // Get the dispersion points for the length
290        vector<WeightPoint> weights_len;
291        length.get_weights(weights_len);
292
293        // Get angular averaging for theta
294        vector<WeightPoint> weights_theta;
295        cyl_theta.get_weights(weights_theta);
296
297        // Get angular averaging for phi
298        vector<WeightPoint> weights_phi;
299        cyl_phi.get_weights(weights_phi);
300
301        // Perform the computation, with all weight points
302        double sum = 0.0;
303        double norm = 0.0;
[c451be9]304        double norm_vol = 0.0;
305        double vol = 0.0;
[4628e31]306        double pi = 4.0*atan(1.0);
[fca6936]307        // Loop over radius weight points
[34c2649]308        for(size_t i=0; i<weights_rad.size(); i++) {
[fca6936]309                dp.radius = weights_rad[i].value;
310
311
312                // Loop over length weight points
[34c2649]313                for(size_t j=0; j<weights_len.size(); j++) {
[fca6936]314                        dp.length = weights_len[j].value;
315
316                        // Average over theta distribution
[34c2649]317                        for(size_t k=0; k<weights_theta.size(); k++) {
[fca6936]318                                dp.cyl_theta = weights_theta[k].value;
319
320                                // Average over phi distribution
[34c2649]321                                for(size_t l=0; l<weights_phi.size(); l++) {
[fca6936]322                                        dp.cyl_phi = weights_phi[l].value;
[c451be9]323                                        //Un-normalize by volume
[fca6936]324                                        double _ptvalue = weights_rad[i].weight
325                                                * weights_len[j].weight
326                                                * weights_theta[k].weight
327                                                * weights_phi[l].weight
[c451be9]328                                                * cylinder_analytical_2DXY(&dp, qx, qy)
329                                                *pow(weights_rad[i].value,2)*weights_len[j].value;
[fca6936]330                                        if (weights_theta.size()>1) {
[318b5bbb]331                                                _ptvalue *= fabs(cos(weights_theta[k].value*pi/180.0));
[fca6936]332                                        }
333                                        sum += _ptvalue;
[c451be9]334                                        //Find average volume
335                                        vol += weights_rad[i].weight
336                                                        * weights_len[j].weight
337                                                        * pow(weights_rad[i].value,2)*weights_len[j].value;
338                                        //Find norm for volume
339                                        norm_vol += weights_rad[i].weight
340                                                        * weights_len[j].weight;
[fca6936]341
342                                        norm += weights_rad[i].weight
343                                                * weights_len[j].weight
344                                                * weights_theta[k].weight
345                                                * weights_phi[l].weight;
346
347                                }
348                        }
349                }
350        }
351        // Averaging in theta needs an extra normalization
352        // factor to account for the sin(theta) term in the
353        // integration (see documentation).
354        if (weights_theta.size()>1) norm = norm / asin(1.0);
[c451be9]355        if (vol != 0.0 && norm_vol != 0.0) {
356                //Re-normalize by avg volume
357                sum = sum/(vol/norm_vol);}
358
[fca6936]359        return sum/norm + background();
360}
361
362/**
363 * Function to evaluate 2D scattering function
364 * @param pars: parameters of the cylinder
365 * @param q: q-value
366 * @param phi: angle phi
367 * @return: function value
368 */
[af03ddd]369double CylinderModel :: evaluate_rphi(double q, double phi) {
[fca6936]370        double qx = q*cos(phi);
371        double qy = q*sin(phi);
372        return (*this).operator()(qx, qy);
373}
[f9bf661]374/**
375 * Function to calculate effective radius
376 * @return: effective radius value
377 */
378double CylinderModel :: calculate_ER() {
379        CylinderParameters dp;
380
381        dp.radius     = radius();
382        dp.length     = length();
383        double rad_out = 0.0;
384
385        // Perform the computation, with all weight points
386        double sum = 0.0;
387        double norm = 0.0;
388
389        // Get the dispersion points for the major shell
390        vector<WeightPoint> weights_length;
391        length.get_weights(weights_length);
392
393        // Get the dispersion points for the minor shell
394        vector<WeightPoint> weights_radius ;
395        radius.get_weights(weights_radius);
[fca6936]396
[f9bf661]397        // Loop over major shell weight points
398        for(int i=0; i< (int)weights_length.size(); i++) {
399                dp.length = weights_length[i].value;
400                for(int k=0; k< (int)weights_radius.size(); k++) {
401                        dp.radius = weights_radius[k].value;
402                        //Note: output of "DiamCyl(dp.length,dp.radius)" is DIAMETER.
403                        sum +=weights_length[i].weight
404                                * weights_radius[k].weight*DiamCyl(dp.length,dp.radius)/2.0;
405                        norm += weights_length[i].weight* weights_radius[k].weight;
406                }
407        }
408        if (norm != 0){
409                //return the averaged value
410                rad_out =  sum/norm;}
411        else{
412                //return normal value
413                //Note: output of "DiamCyl(dp.length,dp.radius)" is DIAMETER.
414                rad_out = DiamCyl(dp.length,dp.radius)/2.0;}
415
416        return rad_out;
417}
[e08bd5b]418double CylinderModel :: calculate_VR() {
419  return 1.0;
420}
Note: See TracBrowser for help on using the repository browser.