source: sasview/sansmodels/src/c_models/csparallelepiped.cpp @ 3aab96b

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 3aab96b was e08bd5b, checked in by Jae Cho <jhjcho@…>, 13 years ago

c models fix: scale fix for P*S

  • Property mode set to 100644
File size: 16.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 2010, 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#include <math.h>
22#include "parameters.hh"
23#include <stdio.h>
24using namespace std;
25
26extern "C" {
27#include "libCylinder.h"
28#include "libStructureFactor.h"
29}
30#include "csparallelepiped.h"
31
32// Convenience parameter structure
33typedef struct {
34  double scale;
35  double shortA;
36  double midB;
37  double longC;
38  double rimA;
39  double rimB;
40  double rimC;
41  double sld_rimA;
42  double sld_rimB;
43  double sld_rimC;
44  double sld_pcore;
45  double sld_solv;
46  double background;
47  double parallel_theta;
48  double parallel_phi;
49  double parallel_psi;
50} CSParallelepipedParameters;
51
52static double cspkernel(double dp[],double q, double ala, double alb, double alc){
53  // mu passed in is really mu*sqrt(1-sig^2)
54  double argA,argB,argC,argtA,argtB,argtC,tmp1,tmp2,tmp3,tmpt1,tmpt2,tmpt3;     //local variables
55
56  double aa,bb,cc, ta,tb,tc;
57  double Vin,Vot,V1,V2,V3;
58  double rhoA,rhoB,rhoC, rhoP, rhosolv;
59  double dr0, drA,drB, drC;
60  double retVal;
61
62  aa = dp[1];
63  bb = dp[2];
64  cc = dp[3];
65  ta = dp[4];
66  tb = dp[5];
67  tc = dp[6];
68  rhoA=dp[7];
69  rhoB=dp[8];
70  rhoC=dp[9];
71  rhoP=dp[10];
72  rhosolv=dp[11];
73  dr0=rhoP-rhosolv;
74  drA=rhoA-rhosolv;
75  drB=rhoB-rhosolv;
76  drC=rhoC-rhosolv;
77  Vin=(aa*bb*cc);
78  Vot=(aa*bb*cc+2.0*ta*bb*cc+2.0*aa*tb*cc+2.0*aa*bb*tc);
79  V1=(2.0*ta*bb*cc);   //  incorrect V1 (aa*bb*cc+2*ta*bb*cc)
80  V2=(2.0*aa*tb*cc);  // incorrect V2(aa*bb*cc+2*aa*tb*cc)
81  V3=(2.0*aa*bb*tc);
82  //aa = aa/bb;
83  ta=(aa+2.0*ta);///bb;
84  tb=(aa+2.0*tb);///bb;
85  tc=(aa+2.0*tc);
86  //handle arg=0 separately, as sin(t)/t -> 1 as t->0
87  argA = q*aa*ala/2.0;
88  argB = q*bb*alb/2.0;
89  argC = q*cc*alc/2.0;
90  argtA = q*ta*ala/2.0;
91  argtB = q*tb*alb/2.0;
92  argtC = q*tc*alc/2.0;
93
94  if(argA==0.0) {
95    tmp1 = 1.0;
96  } else {
97    tmp1 = sin(argA)/argA;
98  }
99  if (argB==0.0) {
100    tmp2 = 1.0;
101  } else {
102    tmp2 = sin(argB)/argB;
103  }
104
105  if (argC==0.0) {
106    tmp3 = 1.0;
107  } else {
108    tmp3 = sin(argC)/argC;
109  }
110  if(argtA==0.0) {
111    tmpt1 = 1.0;
112  } else {
113    tmpt1 = sin(argtA)/argtA;
114  }
115  if (argtB==0.0) {
116    tmpt2 = 1.0;
117  } else {
118    tmpt2 = sin(argtB)/argtB;
119  }
120  if (argtC==0.0) {
121    tmpt3 = 1.0;
122  } else {
123    tmpt3 = sin(argtC)*sin(argtC)/argtC/argtC;
124  }
125  // This expression is different from NIST/IGOR package (I strongly believe the IGOR is wrong!!!). 10/15/2010.
126  retVal =( dr0*tmp1*tmp2*tmp3*Vin + drA*(tmpt1-tmp1)*tmp2*tmp3*V1+ drB*tmp1*(tmpt2-tmp2)*tmp3*V2 + drC*tmp1*tmp2*(tmpt3-tmp3)*V3)*
127      ( dr0*tmp1*tmp2*tmp3*Vin + drA*(tmpt1-tmp1)*tmp2*tmp3*V1+ drB*tmp1*(tmpt2-tmp2)*tmp3*V2 + drC*tmp1*tmp2*(tmpt3-tmp3)*V3);   //  correct FF : square of sum of phase factors
128  //retVal *= (tmp3*tmp3);
129  retVal /= Vot;
130
131  return (retVal);
132
133}//Function cspkernel()
134
135/**
136 * Function to evaluate 2D scattering function
137 * @param pars: parameters of the CSparallelepiped
138 * @param q: q-value
139 * @param q_x: q_x / q
140 * @param q_y: q_y / q
141 * @return: function value
142 */
143static double csparallelepiped_analytical_2D_scaled(CSParallelepipedParameters *pars, double q, double q_x, double q_y) {
144  double dp[13];
145  double cparallel_x, cparallel_y, cparallel_z, bparallel_x, bparallel_y, parallel_x, parallel_y;
146  double q_z;
147  double alpha, cos_val_c, cos_val_b, cos_val_a, edgeA, edgeB, edgeC;
148
149  double answer;
150  //convert angle degree to radian
151  double pi = 4.0*atan(1.0);
152  double theta = pars->parallel_theta * pi/180.0;
153  double phi = pars->parallel_phi * pi/180.0;
154  double psi = pars->parallel_psi* pi/180.0;
155
156  // Fill paramater array
157  dp[0] = 1.0;
158  dp[1] = pars->shortA;
159  dp[2] = pars->midB;
160  dp[3] = pars->longC;
161  dp[4] = pars->rimA;
162  dp[5] = pars->rimB;
163  dp[6] = pars->rimC;
164  dp[7] = pars->sld_rimA;
165  dp[8] = pars->sld_rimB;
166  dp[9] = pars->sld_rimC;
167  dp[10] = pars->sld_pcore;
168  dp[11] = pars->sld_solv;
169  dp[12] = 0.0;
170
171
172  edgeA = pars->shortA;
173  edgeB = pars->midB;
174  edgeC = pars->longC;
175
176
177  // parallelepiped c axis orientation
178  cparallel_x = sin(theta) * cos(phi);
179  cparallel_y = sin(theta) * sin(phi);
180  cparallel_z = cos(theta);
181
182  // q vector
183  q_z = 0.0;
184
185  // Compute the angle btw vector q and the
186  // axis of the parallelepiped
187  cos_val_c = cparallel_x*q_x + cparallel_y*q_y + cparallel_z*q_z;
188  alpha = acos(cos_val_c);
189
190  // parallelepiped a axis orientation
191  parallel_x = sin(psi);//cos(pars->parallel_theta) * sin(pars->parallel_phi)*sin(pars->parallel_psi);
192  parallel_y = cos(psi);//cos(pars->parallel_theta) * cos(pars->parallel_phi)*cos(pars->parallel_psi);
193
194  cos_val_a = parallel_x*q_x + parallel_y*q_y;
195
196
197
198  // parallelepiped b axis orientation
199  bparallel_x = sqrt(1.0-sin(theta)*cos(phi))*cos(psi);//cos(pars->parallel_theta) * cos(pars->parallel_phi)* cos(pars->parallel_psi);
200  bparallel_y = sqrt(1.0-sin(theta)*cos(phi))*sin(psi);//cos(pars->parallel_theta) * sin(pars->parallel_phi)* sin(pars->parallel_psi);
201  // axis of the parallelepiped
202  cos_val_b = sin(acos(cos_val_a)) ;
203
204
205
206  // The following test should always pass
207  if (fabs(cos_val_c)>1.0) {
208    printf("parallel_ana_2D: Unexpected error: cos(alpha)>1\n");
209    return 0;
210  }
211
212  // Call the IGOR library function to get the kernel
213  answer = cspkernel( dp,q, sin(alpha)*cos_val_a,sin(alpha)*cos_val_b,cos_val_c);
214
215  //convert to [cm-1]
216  answer *= 1.0e8;
217
218  //Scale
219  answer *= pars->scale;
220
221  // add in the background
222  answer += pars->background;
223
224  return answer;
225}
226
227/**
228 * Function to evaluate 2D scattering function
229 * @param pars: parameters of the CSparallelepiped
230 * @param q: q-value
231 * @return: function value
232 */
233static double csparallelepiped_analytical_2DXY(CSParallelepipedParameters *pars, double qx, double qy) {
234  double q;
235  q = sqrt(qx*qx+qy*qy);
236  return csparallelepiped_analytical_2D_scaled(pars, q, qx/q, qy/q);
237}
238
239
240
241
242CSParallelepipedModel :: CSParallelepipedModel() {
243  scale      = Parameter(1.0);
244  shortA     = Parameter(35.0, true);
245  shortA.set_min(1.0);
246  midB     = Parameter(75.0, true);
247  midB.set_min(1.0);
248  longC    = Parameter(400.0, true);
249  longC.set_min(1.0);
250  rimA     = Parameter(10.0, true);
251  rimB     = Parameter(10.0, true);
252  rimC     = Parameter(10.0, true);
253  sld_rimA     = Parameter(2.0e-6, true);
254  sld_rimB     = Parameter(4.0e-6, true);
255  sld_rimC    = Parameter(2.0e-6, true);
256  sld_pcore   = Parameter(1.0e-6);
257  sld_solv   = Parameter(6.0e-6);
258  background = Parameter(0.06);
259  parallel_theta  = Parameter(0.0, true);
260  parallel_phi    = Parameter(0.0, true);
261  parallel_psi    = Parameter(0.0, true);
262}
263
264/**
265 * Function to evaluate 1D scattering function
266 * The NIST IGOR library is used for the actual calculation.
267 * @param q: q-value
268 * @return: function value
269 */
270double CSParallelepipedModel :: operator()(double q) {
271  double dp[13];
272
273  // Fill parameter array for IGOR library
274  // Add the background after averaging
275  dp[0] = scale();
276  dp[1] = shortA();
277  dp[2] = midB();
278  dp[3] = longC();
279  dp[4] = rimA();
280  dp[5] = rimB();
281  dp[6] = rimC();
282  dp[7] = sld_rimA();
283  dp[8] = sld_rimB();
284  dp[9] = sld_rimC();
285  dp[10] = sld_pcore();
286  dp[11] = sld_solv();
287  dp[12] = 0.0;
288
289  // Get the dispersion points for the short_edgeA
290  vector<WeightPoint> weights_shortA;
291  shortA.get_weights(weights_shortA);
292
293  // Get the dispersion points for the longer_edgeB
294  vector<WeightPoint> weights_midB;
295  midB.get_weights(weights_midB);
296
297  // Get the dispersion points for the longuest_edgeC
298  vector<WeightPoint> weights_longC;
299  longC.get_weights(weights_longC);
300
301
302
303  // Perform the computation, with all weight points
304  double sum = 0.0;
305  double norm = 0.0;
306  double vol = 0.0;
307
308  // Loop over short_edgeA weight points
309  for(int i=0; i< (int)weights_shortA.size(); i++) {
310    dp[1] = weights_shortA[i].value;
311
312    // Loop over longer_edgeB weight points
313    for(int j=0; j< (int)weights_midB.size(); j++) {
314      dp[2] = weights_midB[j].value;
315
316      // Loop over longuest_edgeC weight points
317      for(int k=0; k< (int)weights_longC.size(); k++) {
318        dp[3] = weights_longC[k].value;
319        //Un-normalize  by volume
320        sum += weights_shortA[i].weight * weights_midB[j].weight
321            * weights_longC[k].weight * CSParallelepiped(dp, q)
322        * weights_shortA[i].value*weights_midB[j].value
323        * weights_longC[k].value;
324        //Find average volume
325        vol += weights_shortA[i].weight * weights_midB[j].weight
326            * weights_longC[k].weight
327            * weights_shortA[i].value * weights_midB[j].value
328            * weights_longC[k].value;
329
330        norm += weights_shortA[i].weight
331            * weights_midB[j].weight * weights_longC[k].weight;
332      }
333    }
334  }
335  if (vol != 0.0 && norm != 0.0) {
336    //Re-normalize by avg volume
337    sum = sum/(vol/norm);}
338
339  return sum/norm + background();
340}
341/**
342 * Function to evaluate 2D scattering function
343 * @param q_x: value of Q along x
344 * @param q_y: value of Q along y
345 * @return: function value
346 */
347double CSParallelepipedModel :: operator()(double qx, double qy) {
348  CSParallelepipedParameters dp;
349  // Fill parameter array
350  dp.scale      = scale();
351  dp.shortA   = shortA();
352  dp.midB   = midB();
353  dp.longC  = longC();
354  dp.rimA   = rimA();
355  dp.rimB   = rimB();
356  dp.rimC  = rimC();
357  dp.sld_rimA   = sld_rimA();
358  dp.sld_rimB   = sld_rimB();
359  dp.sld_rimC  = sld_rimC();
360  dp.sld_pcore   = sld_pcore();
361  dp.sld_solv   = sld_solv();
362  dp.background = 0.0;
363  //dp.background = background();
364  dp.parallel_theta  = parallel_theta();
365  dp.parallel_phi    = parallel_phi();
366  dp.parallel_psi    = parallel_psi();
367
368
369
370  // Get the dispersion points for the short_edgeA
371  vector<WeightPoint> weights_shortA;
372  shortA.get_weights(weights_shortA);
373
374  // Get the dispersion points for the longer_edgeB
375  vector<WeightPoint> weights_midB;
376  midB.get_weights(weights_midB);
377
378  // Get the dispersion points for the longuest_edgeC
379  vector<WeightPoint> weights_longC;
380  longC.get_weights(weights_longC);
381
382  // Get angular averaging for theta
383  vector<WeightPoint> weights_parallel_theta;
384  parallel_theta.get_weights(weights_parallel_theta);
385
386  // Get angular averaging for phi
387  vector<WeightPoint> weights_parallel_phi;
388  parallel_phi.get_weights(weights_parallel_phi);
389
390  // Get angular averaging for psi
391  vector<WeightPoint> weights_parallel_psi;
392  parallel_psi.get_weights(weights_parallel_psi);
393
394  // Perform the computation, with all weight points
395  double sum = 0.0;
396  double norm = 0.0;
397  double norm_vol = 0.0;
398  double vol = 0.0;
399  double pi = 4.0*atan(1.0);
400
401  // Loop over radius weight points
402  for(int i=0; i< (int)weights_shortA.size(); i++) {
403    dp.shortA = weights_shortA[i].value;
404
405    // Loop over longer_edgeB weight points
406    for(int j=0; j< (int)weights_midB.size(); j++) {
407      dp.midB = weights_midB[j].value;
408
409      // Average over longuest_edgeC distribution
410      for(int k=0; k< (int)weights_longC.size(); k++) {
411        dp.longC = weights_longC[k].value;
412
413        // Average over theta distribution
414        for(int l=0; l< (int)weights_parallel_theta.size(); l++) {
415          dp.parallel_theta = weights_parallel_theta[l].value;
416
417          // Average over phi distribution
418          for(int m=0; m< (int)weights_parallel_phi.size(); m++) {
419            dp.parallel_phi = weights_parallel_phi[m].value;
420
421            // Average over phi distribution
422            for(int n=0; n< (int)weights_parallel_psi.size(); n++) {
423              dp.parallel_psi = weights_parallel_psi[n].value;
424              //Un-normalize by volume
425              double _ptvalue = weights_shortA[i].weight
426                  * weights_midB[j].weight
427                  * weights_longC[k].weight
428                  * weights_parallel_theta[l].weight
429                  * weights_parallel_phi[m].weight
430                  * weights_parallel_psi[n].weight
431                  * csparallelepiped_analytical_2DXY(&dp, qx, qy)
432              * weights_shortA[i].value*weights_midB[j].value
433              * weights_longC[k].value;
434
435              if (weights_parallel_theta.size()>1) {
436                _ptvalue *= fabs(sin(weights_parallel_theta[l].value*pi/180.0));
437              }
438              sum += _ptvalue;
439              //Find average volume
440              vol += weights_shortA[i].weight
441                  * weights_midB[j].weight
442                  * weights_longC[k].weight
443                  * weights_shortA[i].value*weights_midB[j].value
444                  * weights_longC[k].value;
445              //Find norm for volume
446              norm_vol += weights_shortA[i].weight
447                  * weights_midB[j].weight
448                  * weights_longC[k].weight;
449
450              norm += weights_shortA[i].weight
451                  * weights_midB[j].weight
452                  * weights_longC[k].weight
453                  * weights_parallel_theta[l].weight
454                  * weights_parallel_phi[m].weight
455                  * weights_parallel_psi[n].weight;
456            }
457          }
458
459        }
460      }
461    }
462  }
463  // Averaging in theta needs an extra normalization
464  // factor to account for the sin(theta) term in the
465  // integration (see documentation).
466  if (weights_parallel_theta.size()>1) norm = norm / asin(1.0);
467
468  if (vol != 0.0 && norm_vol != 0.0) {
469    //Re-normalize by avg volume
470    sum = sum/(vol/norm_vol);}
471
472  return sum/norm + background();
473}
474
475
476/**
477 * Function to evaluate 2D scattering function
478 * @param pars: parameters of the cylinder
479 * @param q: q-value
480 * @param phi: angle phi
481 * @return: function value
482 */
483double CSParallelepipedModel :: evaluate_rphi(double q, double phi) {
484  double qx = q*cos(phi);
485  double qy = q*sin(phi);
486  return (*this).operator()(qx, qy);
487}
488/**
489 * Function to calculate effective radius
490 * @return: effective radius value
491 */
492double CSParallelepipedModel :: calculate_ER() {
493  CSParallelepipedParameters dp;
494  dp.shortA   = shortA();
495  dp.midB   = midB();
496  dp.longC  = longC();
497  dp.rimA   = rimA();
498  dp.rimB   = rimB();
499  dp.rimC  = rimC();
500
501  double rad_out = 0.0;
502  double pi = 4.0*atan(1.0);
503  double suf_rad = sqrt((dp.shortA*dp.midB+2.0*dp.rimA*dp.midB+2.0*dp.rimA*dp.shortA)/pi);
504  double height =(dp.longC + 2.0*dp.rimC);
505  // Perform the computation, with all weight points
506  double sum = 0.0;
507  double norm = 0.0;
508
509  // Get the dispersion points for the short_edgeA
510  vector<WeightPoint> weights_shortA;
511  shortA.get_weights(weights_shortA);
512
513  // Get the dispersion points for the longer_edgeB
514  vector<WeightPoint> weights_midB;
515  midB.get_weights(weights_midB);
516
517  // Get angular averaging for the longuest_edgeC
518  vector<WeightPoint> weights_longC;
519  longC.get_weights(weights_longC);
520
521  // Loop over radius weight points
522  for(int i=0; i< (int)weights_shortA.size(); i++) {
523    dp.shortA = weights_shortA[i].value;
524
525    // Loop over longer_edgeB weight points
526    for(int j=0; j< (int)weights_midB.size(); j++) {
527      dp.midB = weights_midB[j].value;
528
529      // Average over longuest_edgeC distribution
530      for(int k=0; k< (int)weights_longC.size(); k++) {
531        dp.longC = weights_longC[k].value;
532        //Calculate surface averaged radius
533        //This is rough approximation.
534        suf_rad = sqrt((dp.shortA*dp.midB+2.0*dp.rimA*dp.midB+2.0*dp.rimA*dp.shortA)/pi);
535        height =(dp.longC + 2.0*dp.rimC);
536        //Note: output of "DiamCyl(dp.length,dp.radius)" is a DIAMETER.
537        sum +=weights_shortA[i].weight* weights_midB[j].weight
538            * weights_longC[k].weight*DiamCyl(height, suf_rad)/2.0;
539        norm += weights_shortA[i].weight* weights_midB[j].weight*weights_longC[k].weight;
540      }
541    }
542  }
543
544  if (norm != 0){
545    //return the averaged value
546    rad_out =  sum/norm;}
547  else{
548    //return normal value
549    //Note: output of "DiamCyl(length,radius)" is DIAMETER.
550    rad_out = DiamCyl(dp.longC, suf_rad)/2.0;}
551  return rad_out;
552
553}
554double CSParallelepipedModel :: calculate_VR() {
555  return 1.0;
556}
Note: See TracBrowser for help on using the repository browser.