File: SANS_spheres2.comp

package info (click to toggle)
mccode 3.5.19%2Bds5-2
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,113,256 kB
  • sloc: ansic: 40,697; python: 25,137; yacc: 8,438; sh: 5,405; javascript: 4,596; lex: 1,632; cpp: 742; perl: 296; lisp: 273; makefile: 226; fortran: 132
file content (362 lines) | stat: -rw-r--r-- 11,420 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/*******************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright 1997-2003, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Component: SANS_spheres2
*
* %I
* Written by: P. Willendrup, derived from H. Frielinghaus SANS_benchmark2
* Date: 16.12.2019
* Origin: DTU
*
* %D
* Sample for Small Angle Neutron Scattering - hard spheres in thin solution, mono disperse.
*
* For the scattering simulation a high fraction of neutron paths is directed to the scattering (exact fraction is sc_aim).
* The remaining paths are used for the transmitted beams. The absolute intensities are treated accordingly, and the p-parameter is set accordingly.
*
* For the scattering probability, the integral of the scattering function between Q = 0.0001 and 1.0 AA-1 is calculated.
* This is used in terms of transmisson, and of course for the scattering probability.
* In this way, multiple scattering processes could be treated as well.
*
* The typical SANS range was considered to be between 0.0001 and 1.0 AA-1.
* This means that the scattered neutrons are equally distributed in this range on logarithmic Q-scales.
*
* Example: SANS_spheres2(xwidth=0.01, yheight=0.01, zthick=0.001, model=1.0, dsdw_inc=0.02, sc_aim=0.97, sans_aim=0.95, R-150)
*
* %P
*
* INPUT PARAMETERS
*
* xwidth:       [m]  Width of sample volume 
* yheight:      [m]  Height of sample volume 
* zthick:       [m]  Thickness of sample volume 
* R:           [AA]  Radius of dilute, monodisperse spheres 
* phi:          [1]  Volume-ratio of the spheres wrt. solution
* drho:     [cm^-2]  Scattering length density
* dsdw_inc: [cm^-1]  The incoherent background from the overall sample, should read ca. 1.0 for water, 0.5 for half D2O, half H2O, and ca. 0.02 for D2O
* sc_aim:       [1]  The fraction of neutron paths used to represent the scattered neutrons (including everything: incoherent and coherent). rest is transmission.
* sans_aim:     [1]  The fraction of neutron paths used to represent the scattered neutrons in the sans-range (up to 1.0AA-1). rest is incoherent with Q>1AA-1.
* singlesp:     [1]  Switches between multiple scattering (parameter zero 0.0) and single scattering (parameter 1.0). The sc_aim directs a fraction of paths to the first scattering process accordingly. The no. of paths for the second scattering process is derived from the real probability. Up to 10 scattering processes are considered.
* Qmind:    [AA^-1]  Lower limit of "SANS" scattering
* Qmaxd:    [AA^-1]  Upper limit of "SANS" scattering
*
* %Link
*
* %E
*******************************************************************************/

DEFINE COMPONENT SANS_spheres2

SETTING PARAMETERS (xwidth=0.01, yheight=0.01, zthick=0.001, dsdw_inc=0.02, sc_aim=0.97, sans_aim=0.95, R=150, phi=1e-3, drho=6e10, int singlesp=1, Qmind = 0.0001, Qmaxd = 2.1544346900319)


SHARE
%{

#pragma acc routine seq
double Min(double A, double B) {
if (A<B) return A; else return B;
};

#pragma acc routine seq
double Max(double A, double B) {
if (A>B) return A; else return B;
};

#pragma acc routine seq
int IMin(int A, int B) {
if (A<B) return A; else return B;
};

#pragma acc routine seq
int IMax(int A, int B) {
if (A>B) return A; else return B;
};
 
#pragma acc routine seq
 double dSigdW(double Q, double R, double phi, double drho) {

  double out;
  double G;  
  double qR;
  
  qR  = Q*R;
  G   = (drho*drho*phi*4e-24*PI*R*R*R/3.0); /* 4 is from sphere volume, 1e-24 is AA^3->cm^3  */

  /* Note that for very small q, we should rather do a Taylor expansion here. 
     - See H. Frielinghaus mail to PW, WGB from Dec. 18th 2019 */
  out  = 3.0*(sin(qR)-qR*cos(qR))/(qR*qR*qR);
  out *= G * out;
  
  return out;
}
%}


DECLARE
%{
  DArray1d Idsdw;
  double Qminl;
  double Qmaxl; 
  double l10;  /* logarithms of Qmind, Qmaxd and constant ln(10) */
  double p0;
%}

INITIALIZE
%{

  if (!xwidth || !yheight || !zthick)
  {
    exit(fprintf(stderr,"%s:	 sample has no volume (zero dimensions)\n", NAME_CURRENT_COMP));
  }

  int iii,kkk;

  Qminl = log10(Qmind);
  Qmaxl = log10(Qmaxd);
  l10   = log(10.00);

  double q,Isq;
  double qmin,qmax,step;
  int    istp;

  istp = floor((Qmaxl-Qminl)*300.0+0.5);

  Idsdw = create_darr1d(31);

  /* By integration, calculate the coherent scattering cross-section for the relevant wavelength range */
  for (iii=1;iii<=30;iii++) {                             /* wavelength in AA, up to 30 */
    Idsdw[iii] = 0.0;
    Isq  = 0.0;
    qmin = 0.0;
    step = (log10(Min(Qmaxd,4.0*PI/iii))-Qminl)/istp;
    for (kkk=0;kkk<=istp;kkk++) {
      qmax = pow(10.0,Qminl+kkk*step);
      q    = 0.5*(qmin+qmax);
      Isq += dSigdW(q,R,phi,drho)*q*(qmax-qmin);
      qmin = qmax;
    };
    Idsdw[iii]= Isq;
  };

%}

TRACE
%{
  double v,k0,lambda;
  int    Ilam,Ilam2;
  double qmax,qmaxl,Ymax,Xmax,thmax;

  /* Wavelength-dependent cross-section variables for cross-section terms */
  double Scoh, Sinc1, Sinc2, Stot;
  
  double rcut,fcut;
  double Q, Xsc, theta;
  int    iscatt;

  char   intersect;
  double t0, t1, dt, phiROT;

  double axis_x, axis_y, axis_z;
  double tmp_vx, tmp_vy, tmp_vz, vout_x, vout_y, vout_z;

  /* Initial neutron weight saved for later */
  p0=p;

  /* Number of scatterings in sample - limit at 10 below */
  iscatt = 0;

  v      = sqrt(vx*vx + vy*vy + vz*vz);
  k0     = v / K2V;
  lambda = 2.0*PI / k0;

  Ilam   = IMax(floor(lambda),1);
  Ilam2  = IMin(Ilam+1,30);
  /* Coherent "SANS" scattering  - in 3 intervals, asymptotic values at the low and high WL end */
  if (lambda<=1.0)   Scoh = 200.0*PI*Idsdw[1]  / (k0*k0);
  else {
    if (lambda>=30.0) Scoh = 200.0*PI*Idsdw[30] / (k0*k0);
    else               Scoh = 200.0*PI*((Ilam2-lambda)*Idsdw[Ilam]+(lambda-Ilam)*Idsdw[Ilam2]) / (k0*k0);
  };

  /* Scattering triangle consideration, limit to lowes of
     either Qmind or double initial k0 value */
  qmax   = Min(Qmaxd,2.0*k0);
  qmaxl  = log10(qmax);
  
  Ymax   = 0.25*qmax*qmax/(k0*k0);
  /* Maximal relative scale between q and k0 */
  if (Ymax>=0.9999) Ymax=1.0;       /* if rounding errors occurr, this will help to avoid problems */
  Xmax   = 1.0 - 2.0*Ymax;
  /* Maximal scattering angle for SANS signal */
  thmax  = acos(Xmax);
  
  /* Inchoherent "forward" scattering */
  Sinc1  = 100.0*PI*(    qmax*qmax/(k0*k0)) * fabs(dsdw_inc);
  /* non-directional incoherent scattering */
  Sinc2  = 100.0*PI*(4.0-qmax*qmax/(k0*k0)) * fabs(dsdw_inc);

  /* - that result in the total scattering cross-section */
  Stot   = Sinc1 + Sinc2 + Scoh;
  
  /* Check for intersections with sample */
  intersect = box_intersect(&t0, &t1, x, y, z, vx, vy, vz, xwidth, yheight, zthick);

  /* Are we hitting and does cross-section have finite value? */
  if (intersect && Stot>0.0) {

    /*  Kill neutron if it already entered sample volume */
    if(t0<0.0) ABSORB;

    /* Using total XS, check if we should scatter coherently here or transmit. Partition statistics accordingly. */
    rcut   = exp(-Stot*(t1-t0)*v);

    /* Sample scattering position logarithmically */
    if (1.0-rcut > sc_aim) {
      dt = -1.0/(v*Stot)*log(rand01());
    } else {
      if (rand01()<=sc_aim) {
	dt  = -1.0/(v*Stot)*log(1.0-(1.0-rcut)*rand01());
	p  *= (1.0-rcut)/sc_aim;
      }
      else {
	/* Transmit this guy */
	dt  = -1.0/(v*Stot)*log(rcut*rand01());
	dt  = 1e33;                  /* run out of sample ... */
	p  *= rcut/(1.0-sc_aim);
      };
    };
    
    /* Based on time-logic, define if we should treat the neutron or not */
    if (t0+dt<=t1) {
      PROP_DT(t0+dt);
      SCATTER;
      iscatt = 1;

      /* Partition statistics according to "SANS" vs. incoherent scattering */
      fcut   = Max(Ymax,sans_aim);

      /* Scatter SANS or not */
      if (rand01()<=fcut) {
	/* Pick a random Q in the SANS regime - logarithmic sampling */
        Q     = pow(10.0,Qminl+(qmaxl-Qminl)*rand01());
	double dsdw;
	dsdw=dSigdW(Q,R,phi,drho);
        p *= 200.0*PI*Q*Q/(k0*k0)*(qmaxl-Qminl)*l10*(dsdw+fabs(dsdw_inc))/(Stot*fcut);
        Xsc   = 1.0 - 0.5*(Q*Q/(k0*k0));
	/* Scattering angle */
        theta = 2.0 * asin(0.5*Q/k0);
      } else {
	/* Random Q for the incoherent case */
	Xsc   = -1.0 + (Xmax+1.0)*rand01();
        p    *= (1.0-Ymax)/(1.0-fcut);
	/* Scattering angle */
	theta = acos(Xsc);
      }
      /* Azimuthal-symmetrical angle */
      phiROT = 2.0*PI*rand01();

      /* vector product between \vec{v} and vertical */
      vec_prod(axis_x, axis_y, axis_z, vx, vy, vz, 0, 1, 0);
      /* apply the two rotations from above */
      rotate(tmp_vx, tmp_vy, tmp_vz, vx, vy, vz, theta, axis_x, axis_y, axis_z);
      rotate(vout_x, vout_y, vout_z, tmp_vx, tmp_vy, tmp_vz, phiROT, vx, vy, vz);

      vx = vout_x;
      vy = vout_y;
      vz = vout_z;

      /* Check if we should do multiple scattering (still) */
      while (iscatt<10 && singlesp==0) {

	/* re-intersect component geometry */
        intersect = box_intersect(&t0, &t1, x, y, z, vx, vy, vz, xwidth, yheight, zthick);
        if (!intersect) ABSORB;

	/* Logarithmic sampling in time according to Xsect */
        dt   = -1.0/(v*Stot)*log(rand01());

	/* Still inside the sample? */
        if (dt<=t1) {

	  /* Propagate and scatter */ 
          PROP_DT(dt);
          SCATTER;
          iscatt++;

	  /* Apply the same weighting and logic scheme as for single-scattering above */
          fcut   = Max(Ymax,sans_aim);

	  if (rand01()<=fcut) {
	    Q     = pow(10.0,Qminl+(qmaxl-Qminl)*rand01());
	    double dsdw;
	    dsdw=dSigdW(Q,R,phi,drho);
	    p    *= 200.0*PI*Q*Q/(k0*k0)*(qmaxl-Qminl)*l10*(dsdw+fabs(dsdw_inc))/(Stot*fcut);
	    Xsc   = 1.0 - 0.5*(Q*Q/(k0*k0));
	    theta = 2.0 * asin(0.5*Q/k0);
	  }
	  else {
	    Xsc   = -1.0 + (Xmax+1.0)*rand01();
	    p    *= (1.0-Ymax)/(1.0-fcut);
	    theta = acos(Xsc);
	  };
	
	  phiROT = 2.0*PI*rand01();

          vec_prod(axis_x, axis_y, axis_z, vx, vy, vz, 0, 1, 0);
          rotate(tmp_vx, tmp_vy, tmp_vz, vx, vy, vz, theta, axis_x, axis_y, axis_z);
          rotate(vout_x, vout_y, vout_z, tmp_vx, tmp_vy, tmp_vz, phiROT, vx, vy, vz);

          vx = vout_x;
          vy = vout_y;
          vz = vout_z;
        } 
        else break; /* Not in the sample any longer */
      };

      /* Final propagation to last "edge" of the sample box */
      intersect = box_intersect(&t0, &t1, x, y, z, vx, vy, vz, xwidth, yheight, zthick);
      if (!intersect) ABSORB;
      PROP_DT(t1);

    } else {
      PROP_DT(t1); /* Time dt was long enough that we already passed the sample */
    };
  };

%}

MCDISPLAY
%{
  double radius = 0;
  double h = 0;
  
  {
    double xmin = -0.5*xwidth;
    double xmax =  0.5*xwidth;
    double ymin = -0.5*yheight;
    double ymax =  0.5*yheight;
    double zmin = -0.5*zthick;
    double zmax =  0.5*zthick;
    multiline(5, xmin, ymin, zmin,
                 xmax, ymin, zmin,
                 xmax, ymax, zmin,
                 xmin, ymax, zmin,
                 xmin, ymin, zmin);
    multiline(5, xmin, ymin, zmax,
                 xmax, ymin, zmax,
                 xmax, ymax, zmax,
                 xmin, ymax, zmax,
                 xmin, ymin, zmax);
    line(xmin, ymin, zmin, xmin, ymin, zmax);
    line(xmax, ymin, zmin, xmax, ymin, zmax);
    line(xmin, ymax, zmin, xmin, ymax, zmax);
    line(xmax, ymax, zmin, xmax, ymax, zmax);
  }

%}
END