source: sasmodels/sasmodels/models/hardsphere.py @ c1e44e5

Last change on this file since c1e44e5 was c1e44e5, checked in by Paul Kienzle <pkienzle@…>, 5 years ago

Add local link to source files. Refs #1263.

  • Property mode set to 100644
File size: 7.2 KB
Line 
1# Note: model title and parameter table are inserted automatically
2r"""
3Calculates the interparticle structure factor for monodisperse
4spherical particles interacting through hard sphere (excluded volume)
5interactions. This $S(q)$ may also be a reasonable approximation for
6other particle shapes that freely rotate (but see the note below),
7and for moderately polydisperse systems.
8
9.. note::
10
11   This routine is intended for uncharged particles! For charged
12   particles try using the :ref:`hayter-msa` $S(q)$ instead.
13
14.. note::
15
16   Earlier versions of SasView did not incorporate the so-called
17   $\beta(q)$ ("beta") correction [1] for polydispersity and non-sphericity.
18   This is only available in SasView versions 4.2.2 and higher.
19
20radius_effective is the effective hard sphere radius.
21volfraction is the volume fraction occupied by the spheres.
22
23In SasView the effective radius may be calculated from the parameters
24used in the form factor $P(q)$ that this $S(q)$ is combined with.
25
26For numerical stability the computation uses a Taylor series expansion
27at very small $qR$, but there may be a very minor glitch at the
28transition point in some circumstances.
29
30This S(q) uses the Percus-Yevick closure relationship [2] where the
31interparticle potential $U(r)$ is
32
33.. math::
34
35    U(r) = \begin{cases}
36    \infty & r < 2R \\
37    0 & r \geq 2R
38    \end{cases}
39
40where $r$ is the distance from the center of a sphere of a radius $R$.
41
42For a 2D plot, the wave transfer is defined as
43
44.. math::
45
46    q = \sqrt{q_x^2 + q_y^2}
47
48
49References
50----------
51
52.. [#] M Kotlarchyk & S-H Chen, *J. Chem. Phys.*, 79 (1983) 2461-2469
53
54.. [#] J K Percus, J Yevick, *J. Phys. Rev.*, 110, (1958) 1
55
56Authorship and Verification
57----------------------------
58
59* **Author:**
60* **Last Modified by:**
61* **Last Reviewed by:**
62"""
63
64import numpy as np
65from numpy import inf
66
67name = "hardsphere"
68title = "Hard sphere structure factor, with Percus-Yevick closure"
69description = """\
70    [Hard sphere structure factor, with Percus-Yevick closure]
71        Interparticle S(Q) for random, non-interacting spheres.
72    May be a reasonable approximation for other particle shapes
73    that freely rotate, and for moderately polydisperse systems
74    . The "beta(q)" correction is available in versions 4.2.2
75    and higher.
76    radius_effective is the hard sphere radius
77    volfraction is the volume fraction occupied by the spheres.
78"""
79category = "structure-factor"
80structure_factor = True
81single = False # TODO: check
82
83#             ["name", "units", default, [lower, upper], "type","description"],
84parameters = [["radius_effective", "Ang", 50.0, [0, inf], "",
85               "effective radius of hard sphere"],
86              ["volfraction", "", 0.2, [0, 0.74], "",
87               "volume fraction of hard spheres"],
88             ]
89
90Iq = r"""
91      double D,A,B,G,X,X2,X4,S,C,FF,HARDSPH;
92      // these are c compiler instructions, can also put normal code inside the "if else" structure
93      #if FLOAT_SIZE > 4
94      // double precision
95      // orig had 0.2, don't call the variable cutoff as PAK already has one called that!
96      // Must use UPPERCASE name please.
97      // 0.05 better, 0.1 OK
98      #define CUTOFFHS 0.05
99      #else
100      // 0.1 bad, 0.2 OK, 0.3 good, 0.4 better, 0.8 no good
101      #define CUTOFFHS 0.4
102      #endif
103
104      if(fabs(radius_effective) < 1.E-12) {
105               HARDSPH=1.0;
106//printf("HS1 %g: %g\n",q,HARDSPH);
107               return(HARDSPH);
108      }
109      // removing use of pow(xxx,2) and rearranging the calcs
110      // of A, B & G cut ~40% off execution time ( 0.5 to 0.3 msec)
111      X = 1.0/( 1.0 -volfraction);
112      D= X*X;
113      A= (1.+2.*volfraction)*D;
114      A *=A;
115      X=fabs(q*radius_effective*2.0);
116
117      if(X < 5.E-06) {
118                 HARDSPH=1./A;
119//printf("HS2 %g: %g\n",q,HARDSPH);
120                 return(HARDSPH);
121      }
122      X2 =X*X;
123      B = (1.0 +0.5*volfraction)*D;
124      B *= B;
125      B *= -6.*volfraction;
126      G=0.5*volfraction*A;
127
128      if(X < CUTOFFHS) {
129      // RKH Feb 2016, use Taylor series expansion for small X
130      // else no obvious way to rearrange the equations to avoid
131      // needing a very high number of significant figures.
132      // Series expansion found using Mathematica software. Numerical test
133      // in .xls showed terms to X^2 are sufficient
134      // for 5 or 6 significant figures, but I put the X^4 one in anyway
135            //FF = 8*A +6*B + 4*G - (0.8*A +2.0*B/3.0 +0.5*G)*X2 +(A/35. +B/40. +G/50.)*X4;
136            // refactoring the polynomial makes it very slightly faster (0.5 not 0.6 msec)
137            //FF = 8*A +6*B + 4*G + ( -0.8*A -2.0*B/3.0 -0.5*G +(A/35. +B/40. +G/50.)*X2)*X2;
138
139            FF = 8.0*A +6.0*B + 4.0*G + ( -0.8*A -B/1.5 -0.5*G +(A/35. +0.0125*B +0.02*G)*X2)*X2;
140
141            // combining the terms makes things worse at smallest Q in single precision
142            //FF = (8-0.8*X2)*A +(3.0-X2/3.)*2*B + (4+0.5*X2)*G +(A/35. +B/40. +G/50.)*X4;
143            // note that G = -volfraction*A/2, combining this makes no further difference at smallest Q
144            //FF = (8 +2.*volfraction + ( volfraction/4. -0.8 +(volfraction/100. -1./35.)*X2 )*X2 )*A  + (3.0 -X2/3. +X4/40.)*2.*B;
145            HARDSPH= 1./(1. + volfraction*FF );
146//printf("HS3 %g: %g\n",q,HARDSPH);
147            return(HARDSPH);
148      }
149      X4=X2*X2;
150      SINCOS(X,S,C);
151
152// RKH Feb 2016, use version FISH code as is better than original sasview one
153// at small Q in single precision, and more than twice as fast in double.
154      //FF=A*(S-X*C)/X + B*(2.*X*S -(X2-2.)*C -2.)/X2 + G*( (4.*X2*X -24.*X)*S -(X4 -12.*X2 +24.)*C +24. )/X4;
155      // refactoring the polynomial here & above makes it slightly faster
156
157      FF=  (( G*( (4.*X2 -24.)*X*S -(X4 -12.*X2 +24.)*C +24. )/X2 + B*(2.*X*S -(X2-2.)*C -2.) )/X + A*(S-X*C))/X ;
158      HARDSPH= 1./(1. + 24.*volfraction*FF/X2 );
159
160      // changing /X and /X2 to *MX1 and *MX2, no significantg difference?
161      //MX=1.0/X;
162      //MX2=MX*MX;
163      //FF=  (( G*( (4.*X2 -24.)*X*S -(X4 -12.*X2 +24.)*C +24. )*MX2 + B*(2.*X*S -(X2-2.)*C -2.) )*MX + A*(S-X*C)) ;
164      //HARDSPH= 1./(1. + 24.*volfraction*FF*MX2*MX );
165
166// grouping the terms, was about same as sasmodels for single precision issues
167//     FF=A*(S/X-C) + B*(2.*S/X - C +2.0*(C-1.0)/X2) + G*( (4./X -24./X3)*S -(1.0 -12./X2 +24./X4)*C +24./X4 );
168//     HARDSPH= 1./(1. + 24.*volfraction*FF/X2 );
169// remove 1/X2 from final line, take more powers of X inside the brackets, stil bad
170//      FF=A*(S/X3-C/X2) + B*(2.*S/X3 - C/X2 +2.0*(C-1.0)/X4) + G*( (4./X -24./X3)*S -(1.0 -12./X2 +24./X4)*C +24./X4 )/X2;
171//      HARDSPH= 1./(1. + 24.*volfraction*FF );
172//printf("HS4 %g: %g\n",q,HARDSPH);
173      return(HARDSPH);
174   """
175
176def random():
177    """Return a random parameter set for the model."""
178    pars = dict(
179        scale=1, background=0,
180        radius_effective=10**np.random.uniform(1, 4),
181        volfraction=10**np.random.uniform(-2, 0),  # high volume fraction
182    )
183    return pars
184
185# Q=0.001 is in the Taylor series, low Q part, so add Q=0.1,
186# assuming double precision sasview is correct
187tests = [
188    [{'scale': 1.0, 'background' : 0.0, 'radius_effective' : 50.0,
189      'volfraction' : 0.2, 'radius_effective_pd' : 0},
190     [0.001, 0.1], [0.209128, 0.930587]],
191]
192# ADDED by: RKH  ON: 16Mar2016  using equations from FISH as better than
193# orig sasview, see notes above. Added Taylor expansions at small Q.
Note: See TracBrowser for help on using the repository browser.