1 | #/usr/bin/env python |
---|
2 | # -*- coding: utf-8 -*- |
---|
3 | ''' |
---|
4 | *GSASII small angle calculation module* |
---|
5 | ================================== |
---|
6 | |
---|
7 | ''' |
---|
8 | ########### SVN repository information ################### |
---|
9 | # $Date: 2014-01-09 11:09:53 -0600 (Thu, 09 Jan 2014) $ |
---|
10 | # $Author: vondreele $ |
---|
11 | # $Revision: 1186 $ |
---|
12 | # $URL: https://subversion.xray.aps.anl.gov/pyGSAS/trunk/GSASIIsasd.py $ |
---|
13 | # $Id: GSASIIsasd.py 1186 2014-01-09 17:09:53Z vondreele $ |
---|
14 | ########### SVN repository information ################### |
---|
15 | import sys |
---|
16 | import math |
---|
17 | import time |
---|
18 | |
---|
19 | import numpy as np |
---|
20 | import scipy as sp |
---|
21 | import numpy.linalg as nl |
---|
22 | from numpy.fft import ifft, fft, fftshift |
---|
23 | import scipy.special as scsp |
---|
24 | import scipy.interpolate as si |
---|
25 | import scipy.stats as st |
---|
26 | import scipy.optimize as so |
---|
27 | |
---|
28 | import GSASIIpath |
---|
29 | GSASIIpath.SetVersionNumber("$Revision: 1186 $") |
---|
30 | import GSASIIlattice as G2lat |
---|
31 | import GSASIIspc as G2spc |
---|
32 | import GSASIIElem as G2elem |
---|
33 | import GSASIIgrid as G2gd |
---|
34 | import GSASIIIO as G2IO |
---|
35 | import GSASIImath as G2mth |
---|
36 | import pypowder as pyd |
---|
37 | |
---|
38 | # trig functions in degrees |
---|
39 | sind = lambda x: math.sin(x*math.pi/180.) |
---|
40 | asind = lambda x: 180.*math.asin(x)/math.pi |
---|
41 | tand = lambda x: math.tan(x*math.pi/180.) |
---|
42 | atand = lambda x: 180.*math.atan(x)/math.pi |
---|
43 | atan2d = lambda y,x: 180.*math.atan2(y,x)/math.pi |
---|
44 | cosd = lambda x: math.cos(x*math.pi/180.) |
---|
45 | acosd = lambda x: 180.*math.acos(x)/math.pi |
---|
46 | rdsq2d = lambda x,p: round(1.0/math.sqrt(x),p) |
---|
47 | #numpy versions |
---|
48 | npsind = lambda x: np.sin(x*np.pi/180.) |
---|
49 | npasind = lambda x: 180.*np.arcsin(x)/math.pi |
---|
50 | npcosd = lambda x: np.cos(x*math.pi/180.) |
---|
51 | npacosd = lambda x: 180.*np.arccos(x)/math.pi |
---|
52 | nptand = lambda x: np.tan(x*math.pi/180.) |
---|
53 | npatand = lambda x: 180.*np.arctan(x)/np.pi |
---|
54 | npatan2d = lambda y,x: 180.*np.arctan2(y,x)/np.pi |
---|
55 | npT2stl = lambda tth, wave: 2.0*npsind(tth/2.0)/wave |
---|
56 | npT2q = lambda tth,wave: 2.0*np.pi*npT2stl(tth,wave) |
---|
57 | |
---|
58 | ############################################################################### |
---|
59 | #### Particle form factors & volumes as class definitions |
---|
60 | ############################################################################### |
---|
61 | |
---|
62 | class SASDParticles(object): |
---|
63 | def __init__(self,name=None,parNames=None): |
---|
64 | self.name = name |
---|
65 | self.ParmNames = parmNames |
---|
66 | |
---|
67 | def FormFactor(): |
---|
68 | FF = 0. |
---|
69 | return FF |
---|
70 | |
---|
71 | def Volume(): |
---|
72 | Vol = 0. |
---|
73 | return Vol |
---|
74 | |
---|
75 | class Sphere(SASDParticles): |
---|
76 | def __init__(self,name=None,parmNames=None): |
---|
77 | self.Name = name |
---|
78 | if self.Name == None: |
---|
79 | self.Name = 'Sphere' |
---|
80 | self.ParmNames = parmNames |
---|
81 | if self.ParmNames == None: |
---|
82 | self.ParmNames = ['Radius',] |
---|
83 | |
---|
84 | def FormFactor(self,Q,Parms): |
---|
85 | ''' Compute hard sphere form factor - can use numpy arrays |
---|
86 | param float:Q Q value array (usually in A-1) |
---|
87 | param float:R sphere radius (Usually in A - must match Q-1 units) |
---|
88 | returns float: form factors as array as needed |
---|
89 | ''' |
---|
90 | QR = Q*Parms[0] |
---|
91 | return (3./(QR**3))*(np.sin(QR)-(QR*np.cos(QR))) |
---|
92 | |
---|
93 | def Volume(self,Parms): |
---|
94 | ''' Compute volume of sphere |
---|
95 | - numpy array friendly |
---|
96 | param float:R sphere radius |
---|
97 | returns float: volume |
---|
98 | ''' |
---|
99 | return (4./3.)*np.pi*Parms[0]**3 |
---|
100 | |
---|
101 | class Spheroid(SASDParticles): |
---|
102 | def __init__(self,name=None,parmNames=None): |
---|
103 | self.Name = name |
---|
104 | if self.Name == None: |
---|
105 | self.Name = 'Spheroid' |
---|
106 | self.ParmNames = parmNames |
---|
107 | if self.ParmNames == None: |
---|
108 | self.ParmNames = ['Radius','Aspect ratio'] |
---|
109 | |
---|
110 | def FormFactor(self,Q,Parms): |
---|
111 | ''' Compute form factor of cylindrically symmetric ellipsoid (spheroid) |
---|
112 | - can use numpy arrays for R & AR; will return corresponding numpy array |
---|
113 | param float:Q Q value array (usually in A-1) |
---|
114 | param float R: radius along 2 axes of spheroid |
---|
115 | param float AR: aspect ratio so 3rd axis = R*AR |
---|
116 | returns float: form factors as array as needed |
---|
117 | ''' |
---|
118 | R,AR = Parms |
---|
119 | NP = 50 |
---|
120 | if 0.99 < AR < 1.01: |
---|
121 | return SphereFF(Q,R,0) |
---|
122 | else: |
---|
123 | cth = np.linspace(0,1.,NP) |
---|
124 | Rct = R*np.sqrt(1.+(AR**2-1.)*cth**2) |
---|
125 | return np.sqrt(np.sum(SphereFF(Q[:,np.newaxis],Rct,0)**2,axis=1)/NP) |
---|
126 | |
---|
127 | def Volume(self,Parms): |
---|
128 | ''' Compute volume of cylindrically symmetric ellipsoid (spheroid) |
---|
129 | - numpy array friendly |
---|
130 | param float R: radius along 2 axes of spheroid |
---|
131 | param float AR: aspect ratio so radius of 3rd axis = R*AR |
---|
132 | returns float: volume |
---|
133 | ''' |
---|
134 | R,AR = Parms |
---|
135 | return AR*(4./3.)*np.pi*R**3 |
---|
136 | |
---|
137 | ############################################################################### |
---|
138 | #### Particle form factors |
---|
139 | ############################################################################### |
---|
140 | |
---|
141 | def SphereFF(Q,R,dummy=0): |
---|
142 | ''' Compute hard sphere form factor - can use numpy arrays |
---|
143 | param float:Q Q value array (usually in A-1) |
---|
144 | param float:R sphere radius (Usually in A - must match Q-1 units) |
---|
145 | returns float: form factors as array as needed |
---|
146 | ''' |
---|
147 | QR = Q*R |
---|
148 | return (3./(QR**3))*(np.sin(QR)-(QR*np.cos(QR))) |
---|
149 | |
---|
150 | def SpheroidFF(Q,R,AR): |
---|
151 | ''' Compute form factor of cylindrically symmetric ellipsoid (spheroid) |
---|
152 | - can use numpy arrays for R & AR; will return corresponding numpy array |
---|
153 | param float:Q Q value array (usually in A-1) |
---|
154 | param float R: radius along 2 axes of spheroid |
---|
155 | param float AR: aspect ratio so 3rd axis = R*AR |
---|
156 | returns float: form factors as array as needed |
---|
157 | ''' |
---|
158 | NP = 50 |
---|
159 | if 0.99 < AR < 1.01: |
---|
160 | return SphereFF(Q,R,0) |
---|
161 | else: |
---|
162 | cth = np.linspace(0,1.,NP) |
---|
163 | Rct = R*np.sqrt(1.+(AR**2-1.)*cth**2) |
---|
164 | return np.sqrt(np.sum(SphereFF(Q[:,np.newaxis],Rct,0)**2,axis=1)/NP) |
---|
165 | |
---|
166 | def CylinderFF(Q,R,L): |
---|
167 | ''' Compute form factor for cylinders - can use numpy arrays |
---|
168 | param float: Q Q value array (A-1) |
---|
169 | param float: R cylinder radius (A) |
---|
170 | param float: L cylinder length (A) |
---|
171 | returns float: form factor |
---|
172 | ''' |
---|
173 | NP = 200 |
---|
174 | alp = np.linspace(0,np.pi/2.,NP) |
---|
175 | LBessArg = 0.5*Q[:,np.newaxis]*L*np.cos(alp) |
---|
176 | LBess = np.where(LBessArg<1.e-6,1.,np.sin(LBessArg)/LBessArg) |
---|
177 | SBessArg = Q[:,np.newaxis]*R*np.sin(alp) |
---|
178 | SBess = np.where(SBessArg<1.e-6,0.5,scsp.jv(1,SBessArg)/SBessArg) |
---|
179 | return np.sqrt(2.*np.pi*np.sum(np.sin(alp)*(LBess*SBess)**2,axis=1)/NP) |
---|
180 | |
---|
181 | def CylinderDFF(Q,L,D): |
---|
182 | ''' Compute form factor for cylinders - can use numpy arrays |
---|
183 | param float: Q Q value array (A-1) |
---|
184 | param float: L cylinder length (A) |
---|
185 | param float: D cylinder diameter (A) |
---|
186 | returns float: form factor |
---|
187 | ''' |
---|
188 | return CylinderFF(Q,D/2.,L) |
---|
189 | |
---|
190 | def CylinderARFF(Q,R,AR): |
---|
191 | ''' Compute form factor for cylinders - can use numpy arrays |
---|
192 | param float: Q Q value array (A-1) |
---|
193 | param float: R cylinder radius (A) |
---|
194 | param float: AR cylinder aspect ratio = L/D = L/2R |
---|
195 | returns float: form factor |
---|
196 | ''' |
---|
197 | return CylinderFF(Q,R,2.*R*AR) |
---|
198 | |
---|
199 | def UniSphereFF(Q,R,dummy=0): |
---|
200 | Rg = np.sqrt(3./5.)*R |
---|
201 | B = 1.62/(Rg**4) #are we missing *np.pi? 1.62 = 6*(3/5)**2/(4/3) sense? |
---|
202 | QstV = Q/(scsp.erf(Q*Rg/np.sqrt(6)))**3 |
---|
203 | return np.sqrt(np.exp((-Q**2*Rg**2)/3.)+(B/QstV**4)) |
---|
204 | |
---|
205 | def UniRodFF(Q,R,L): |
---|
206 | Rg2 = np.sqrt(R**2/2+L**2/12) |
---|
207 | B2 = np.pi/L |
---|
208 | Rg1 = np.sqrt(3.)*R/2. |
---|
209 | G1 = (2./3.)*R/L |
---|
210 | B1 = 4.*(L+R)/(R**3*L**2) |
---|
211 | QstV = Q/(scsp.erf(Q*Rg2/np.sqrt(6)))**3 |
---|
212 | FF = np.exp(-Q**2*Rg2**2/3.)+(B2/QstV)*np.exp(-Rg1**2*Q**2/3.) |
---|
213 | QstV = Q/(scsp.erf(Q*Rg1/np.sqrt(6)))**3 |
---|
214 | FF += G1*np.exp(-Q**2*Rg1**2/3.)+(B1/QstV**4) |
---|
215 | return np.sqrt(FF) |
---|
216 | |
---|
217 | def UniRodARFF(Q,R,AR): |
---|
218 | return UniRodFF(Q,R,AR*R) |
---|
219 | |
---|
220 | def UniDiskFF(Q,R,T): |
---|
221 | Rg2 = np.sqrt(R**2/2.+T**2/12.) |
---|
222 | B2 = 2./R**2 |
---|
223 | Rg1 = np.sqrt(3.)*T/2. |
---|
224 | RgC2 = 1.1*Rg1 |
---|
225 | G1 = (2./3.)*(T/R)**2 |
---|
226 | B1 = 4.*(T+R)/(R**3*T**2) |
---|
227 | QstV = Q/(scsp.erf(Q*Rg2/np.sqrt(6)))**3 |
---|
228 | FF = np.exp(-Q**2*Rg2**2/3.)+(B2/QstV**2)*np.exp(-RgC2**2*Q**2/3.) |
---|
229 | QstV = Q/(scsp.erf(Q*Rg1/np.sqrt(6)))**3 |
---|
230 | FF += G1*np.exp(-Q**2*Rg1**2/3.)+(B1/QstV**4) |
---|
231 | return np.sqrt(FF) |
---|
232 | |
---|
233 | def UniTubeFF(Q,R,L,T): |
---|
234 | Ri = R-T |
---|
235 | DR2 = R**2-Ri**2 |
---|
236 | Vt = np.pi*DR2*L |
---|
237 | Rg3 = np.sqrt(DR2/2.+L**2/12.) |
---|
238 | B1 = 4.*np.pi**2*(DR2+L*(R+Ri))/Vt**2 |
---|
239 | B2 = np.pi**2*T/Vt |
---|
240 | B3 = np.pi/L |
---|
241 | QstV = Q/(scsp.erf(Q*Rg3/np.sqrt(6)))**3 |
---|
242 | FF = np.exp(-Q**2*Rg3**2/3.)+(B3/QstV)*np.exp(-Q**2*R**2/3.) |
---|
243 | QstV = Q/(scsp.erf(Q*R/np.sqrt(6)))**3 |
---|
244 | FF += (B2/QstV**2)*np.exp(-Q**2*T**2/3.) |
---|
245 | QstV = Q/(scsp.erf(Q*T/np.sqrt(6)))**3 |
---|
246 | FF += B1/QstV**4 |
---|
247 | return np.sqrt(FF) |
---|
248 | |
---|
249 | |
---|
250 | |
---|
251 | |
---|
252 | |
---|
253 | ############################################################################### |
---|
254 | #### Particle volumes |
---|
255 | ############################################################################### |
---|
256 | |
---|
257 | def SphereVol(R): |
---|
258 | ''' Compute volume of sphere |
---|
259 | - numpy array friendly |
---|
260 | param float:R sphere radius |
---|
261 | returns float: volume |
---|
262 | ''' |
---|
263 | return (4./3.)*np.pi*R**3 |
---|
264 | |
---|
265 | def SpheroidVol(R,AR): |
---|
266 | ''' Compute volume of cylindrically symmetric ellipsoid (spheroid) |
---|
267 | - numpy array friendly |
---|
268 | param float R: radius along 2 axes of spheroid |
---|
269 | param float AR: aspect ratio so radius of 3rd axis = R*AR |
---|
270 | returns float: volume |
---|
271 | ''' |
---|
272 | return AR*SphereVol(R) |
---|
273 | |
---|
274 | def CylinderVol(R,L): |
---|
275 | ''' Compute cylinder volume for radius & length |
---|
276 | - numpy array friendly |
---|
277 | param float: R diameter (A) |
---|
278 | param float: L length (A) |
---|
279 | returns float:volume (A^3) |
---|
280 | ''' |
---|
281 | return np.pi*L*R**2 |
---|
282 | |
---|
283 | def CylinderDVol(L,D): |
---|
284 | ''' Compute cylinder volume for length & diameter |
---|
285 | - numpy array friendly |
---|
286 | param float: L length (A) |
---|
287 | param float: D diameter (A) |
---|
288 | returns float:volume (A^3) |
---|
289 | ''' |
---|
290 | return CylinderVol(D/2.,L) |
---|
291 | |
---|
292 | def CylinderARVol(R,AR): |
---|
293 | ''' Compute cylinder volume for radius & aspect ratio = L/D |
---|
294 | - numpy array friendly |
---|
295 | param float: R radius (A) |
---|
296 | param float: AR=L/D=L/2R aspect ratio |
---|
297 | returns float:volume |
---|
298 | ''' |
---|
299 | return CylinderVol(R,2.*R*AR) |
---|
300 | |
---|
301 | |
---|
302 | |
---|
303 | ############################################################################### |
---|
304 | #### Size distribution |
---|
305 | ############################################################################### |
---|
306 | |
---|
307 | def SizeDistribution(Profile,ProfDict,Limits,Substances,Sample,data): |
---|
308 | if data['Size']['logBins']: |
---|
309 | Bins = np.logspace(np.log10(data['Size']['MinDiam']),np.log10(data['Size']['MaxDiam']), |
---|
310 | data['Size']['Nbins']+1,True) |
---|
311 | else: |
---|
312 | Bins = np.linspace(data['Size']['MinDiam'],data['Size']['MaxDiam'], |
---|
313 | data['Size']['Nbins']+1,True) |
---|
314 | Dbins = np.diff(Bins) |
---|
315 | BinMag = Dbins*np.ones_like(Dbins) |
---|
316 | print np.sum(BinMag) |
---|
317 | |
---|
318 | print data['Size'] |
---|
319 | # print Limits |
---|
320 | # print Substances |
---|
321 | # print Sample |
---|
322 | # print Profile |
---|
323 | |
---|