diffpy.srfit 3.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- diffpy/__init__.py +15 -0
- diffpy/srfit/__init__.py +41 -0
- diffpy/srfit/equation/__init__.py +31 -0
- diffpy/srfit/equation/builder.py +702 -0
- diffpy/srfit/equation/equationmod.py +213 -0
- diffpy/srfit/equation/literals/__init__.py +71 -0
- diffpy/srfit/equation/literals/abcs.py +81 -0
- diffpy/srfit/equation/literals/argument.py +76 -0
- diffpy/srfit/equation/literals/literal.py +69 -0
- diffpy/srfit/equation/literals/operators.py +380 -0
- diffpy/srfit/equation/visitors/__init__.py +94 -0
- diffpy/srfit/equation/visitors/argfinder.py +62 -0
- diffpy/srfit/equation/visitors/printer.py +120 -0
- diffpy/srfit/equation/visitors/swapper.py +142 -0
- diffpy/srfit/equation/visitors/validator.py +113 -0
- diffpy/srfit/equation/visitors/visitor.py +62 -0
- diffpy/srfit/exceptions.py +29 -0
- diffpy/srfit/fitbase/__init__.py +53 -0
- diffpy/srfit/fitbase/calculator.py +110 -0
- diffpy/srfit/fitbase/configurable.py +57 -0
- diffpy/srfit/fitbase/constraint.py +117 -0
- diffpy/srfit/fitbase/fitcontribution.py +351 -0
- diffpy/srfit/fitbase/fithook.py +227 -0
- diffpy/srfit/fitbase/fitrecipe.py +826 -0
- diffpy/srfit/fitbase/fitresults.py +674 -0
- diffpy/srfit/fitbase/parameter.py +314 -0
- diffpy/srfit/fitbase/parameterset.py +110 -0
- diffpy/srfit/fitbase/profile.py +405 -0
- diffpy/srfit/fitbase/profilegenerator.py +168 -0
- diffpy/srfit/fitbase/profileparser.py +179 -0
- diffpy/srfit/fitbase/recipeorganizer.py +1074 -0
- diffpy/srfit/fitbase/restraint.py +117 -0
- diffpy/srfit/fitbase/simplerecipe.py +230 -0
- diffpy/srfit/fitbase/validatable.py +59 -0
- diffpy/srfit/interface/__init__.py +33 -0
- diffpy/srfit/interface/interface.py +180 -0
- diffpy/srfit/pdf/__init__.py +24 -0
- diffpy/srfit/pdf/basepdfgenerator.py +296 -0
- diffpy/srfit/pdf/characteristicfunctions.py +432 -0
- diffpy/srfit/pdf/debyepdfgenerator.py +115 -0
- diffpy/srfit/pdf/pdfcontribution.py +311 -0
- diffpy/srfit/pdf/pdfgenerator.py +77 -0
- diffpy/srfit/pdf/pdfparser.py +226 -0
- diffpy/srfit/sas/__init__.py +30 -0
- diffpy/srfit/sas/prcalculator.py +139 -0
- diffpy/srfit/sas/sasgenerator.py +70 -0
- diffpy/srfit/sas/sasimport.py +66 -0
- diffpy/srfit/sas/sasparameter.py +70 -0
- diffpy/srfit/sas/sasparser.py +133 -0
- diffpy/srfit/sas/sasprofile.py +94 -0
- diffpy/srfit/structure/__init__.py +60 -0
- diffpy/srfit/structure/basestructureparset.py +62 -0
- diffpy/srfit/structure/bvsrestraint.py +101 -0
- diffpy/srfit/structure/cctbxparset.py +292 -0
- diffpy/srfit/structure/diffpyparset.py +303 -0
- diffpy/srfit/structure/objcrystparset.py +1552 -0
- diffpy/srfit/structure/sgconstraints.py +761 -0
- diffpy/srfit/structure/srrealparset.py +92 -0
- diffpy/srfit/util/__init__.py +50 -0
- diffpy/srfit/util/argbinders.py +29 -0
- diffpy/srfit/util/inpututils.py +50 -0
- diffpy/srfit/util/nameutils.py +45 -0
- diffpy/srfit/util/observable.py +93 -0
- diffpy/srfit/util/tagmanager.py +150 -0
- diffpy/srfit/util/weakrefcallable.py +170 -0
- diffpy/srfit/version.py +26 -0
- diffpy_srfit-3.1.0.dist-info/METADATA +216 -0
- diffpy_srfit-3.1.0.dist-info/RECORD +73 -0
- diffpy_srfit-3.1.0.dist-info/WHEEL +5 -0
- diffpy_srfit-3.1.0.dist-info/licenses/AUTHORS.rst +10 -0
- diffpy_srfit-3.1.0.dist-info/licenses/LICENSE.rst +138 -0
- diffpy_srfit-3.1.0.dist-info/licenses/LICENSE_DANSE.rst +34 -0
- diffpy_srfit-3.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##############################################################################
|
|
3
|
+
#
|
|
4
|
+
# diffpy.srfit by DANSE Diffraction group
|
|
5
|
+
# Simon J. L. Billinge
|
|
6
|
+
# (c) 2010 The Trustees of Columbia University
|
|
7
|
+
# in the City of New York. All rights reserved.
|
|
8
|
+
#
|
|
9
|
+
# File coded by: Chris Farrow
|
|
10
|
+
#
|
|
11
|
+
# See AUTHORS.txt for a list of people who contributed.
|
|
12
|
+
# See LICENSE_DANSE.txt for license information.
|
|
13
|
+
#
|
|
14
|
+
##############################################################################
|
|
15
|
+
"""Form factors (characteristic functions) used in PDF nanoshape fitting.
|
|
16
|
+
|
|
17
|
+
These are used to calculate the attenuation of the PDF due to a finite
|
|
18
|
+
size. For a crystal-like nanoparticle, one can calculate the PDF via
|
|
19
|
+
Gnano(r) = f(r) Gcryst(r), where f(r) is the nanoparticle characteristic
|
|
20
|
+
function and Gcryst(f) is the crystal PDF.
|
|
21
|
+
|
|
22
|
+
These functions are meant to be imported and added to a FitContribution
|
|
23
|
+
using the 'registerFunction' method of that class.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"sphericalCF",
|
|
28
|
+
"spheroidalCF",
|
|
29
|
+
"spheroidalCF2",
|
|
30
|
+
"lognormalSphericalCF",
|
|
31
|
+
"sheetCF",
|
|
32
|
+
"shellCF",
|
|
33
|
+
"shellCF2",
|
|
34
|
+
"SASCF",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
import numpy
|
|
38
|
+
from numpy import arctan as atan
|
|
39
|
+
from numpy import arctanh as atanh
|
|
40
|
+
from numpy import ceil, exp, log, log2, pi, sign, sqrt
|
|
41
|
+
from numpy.fft import fftfreq, ifft
|
|
42
|
+
from scipy.special import erf
|
|
43
|
+
|
|
44
|
+
from diffpy.srfit.fitbase.calculator import Calculator
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def sphericalCF(r, psize):
|
|
48
|
+
"""Spherical nanoparticle characteristic function.
|
|
49
|
+
|
|
50
|
+
r -- distance of interaction
|
|
51
|
+
psize -- The particle diameter
|
|
52
|
+
|
|
53
|
+
From Kodama et al., Acta Cryst. A, 62, 444-453
|
|
54
|
+
(converted from radius to diameter)
|
|
55
|
+
"""
|
|
56
|
+
f = numpy.zeros(numpy.shape(r), dtype=float)
|
|
57
|
+
if psize > 0:
|
|
58
|
+
x = numpy.array(r, dtype=float) / psize
|
|
59
|
+
inside = x < 1.0
|
|
60
|
+
xin = x[inside]
|
|
61
|
+
f[inside] = 1.0 - 1.5 * xin + 0.5 * xin * xin * xin
|
|
62
|
+
return f
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def spheroidalCF(r, erad, prad):
|
|
66
|
+
"""Spheroidal characteristic function specified using radii.
|
|
67
|
+
|
|
68
|
+
Spheroid with radii (erad, erad, prad)
|
|
69
|
+
|
|
70
|
+
prad -- polar radius
|
|
71
|
+
erad -- equatorial radius
|
|
72
|
+
|
|
73
|
+
erad < prad equates to a prolate spheroid
|
|
74
|
+
erad > prad equates to a oblate spheroid
|
|
75
|
+
erad == prad is a sphere
|
|
76
|
+
"""
|
|
77
|
+
psize = 2.0 * erad
|
|
78
|
+
pelpt = 1.0 * prad / erad
|
|
79
|
+
return spheroidalCF2(r, psize, pelpt)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def spheroidalCF2(r, psize, axrat):
|
|
83
|
+
"""Spheroidal nanoparticle characteristic function.
|
|
84
|
+
|
|
85
|
+
Form factor for ellipsoid with radii (psize/2, psize/2, axrat*psize/2)
|
|
86
|
+
|
|
87
|
+
r -- distance of interaction
|
|
88
|
+
psize -- The equatorial diameter
|
|
89
|
+
axrat -- The ratio of axis lengths
|
|
90
|
+
|
|
91
|
+
From Lei et al., Phys. Rev. B, 80, 024118 (2009)
|
|
92
|
+
"""
|
|
93
|
+
pelpt = 1.0 * axrat
|
|
94
|
+
|
|
95
|
+
if psize <= 0 or pelpt <= 0:
|
|
96
|
+
return numpy.zeros_like(r)
|
|
97
|
+
|
|
98
|
+
# to simplify the equations
|
|
99
|
+
v = pelpt
|
|
100
|
+
d = 1.0 * psize
|
|
101
|
+
d2 = d * d
|
|
102
|
+
v2 = v * v
|
|
103
|
+
|
|
104
|
+
if v == 1:
|
|
105
|
+
return sphericalCF(r, psize)
|
|
106
|
+
|
|
107
|
+
rx = r
|
|
108
|
+
if v < 1:
|
|
109
|
+
|
|
110
|
+
r = rx[rx <= v * psize]
|
|
111
|
+
r2 = r * r
|
|
112
|
+
f1 = (
|
|
113
|
+
1
|
|
114
|
+
- 3 * r / (4 * d * v) * (1 - r2 / (4 * d2) * (1 + 2.0 / (3 * v2)))
|
|
115
|
+
- 3
|
|
116
|
+
* r
|
|
117
|
+
/ (4 * d)
|
|
118
|
+
* (1 - r2 / (4 * d2))
|
|
119
|
+
* v
|
|
120
|
+
/ sqrt(1 - v2)
|
|
121
|
+
* atanh(sqrt(1 - v2))
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
r = rx[numpy.logical_and(rx > v * psize, rx <= psize)]
|
|
125
|
+
r2 = r * r
|
|
126
|
+
f2 = (
|
|
127
|
+
(
|
|
128
|
+
3 * d / (8 * r) * (1 + r2 / (2 * d2)) * sqrt(1 - r2 / d2)
|
|
129
|
+
- 3
|
|
130
|
+
* r
|
|
131
|
+
/ (4 * d)
|
|
132
|
+
* (1 - r2 / (4 * d2))
|
|
133
|
+
* atanh(sqrt(1 - r2 / d2))
|
|
134
|
+
)
|
|
135
|
+
* v
|
|
136
|
+
/ sqrt(1 - v2)
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
r = rx[rx > psize]
|
|
140
|
+
f3 = numpy.zeros_like(r)
|
|
141
|
+
|
|
142
|
+
f = numpy.concatenate((f1, f2, f3))
|
|
143
|
+
|
|
144
|
+
elif v > 1:
|
|
145
|
+
|
|
146
|
+
r = rx[rx <= psize]
|
|
147
|
+
r2 = r * r
|
|
148
|
+
f1 = (
|
|
149
|
+
1
|
|
150
|
+
- 3 * r / (4 * d * v) * (1 - r2 / (4 * d2) * (1 + 2.0 / (3 * v2)))
|
|
151
|
+
- 3
|
|
152
|
+
* r
|
|
153
|
+
/ (4 * d)
|
|
154
|
+
* (1 - r2 / (4 * d2))
|
|
155
|
+
* v
|
|
156
|
+
/ sqrt(v2 - 1)
|
|
157
|
+
* atan(sqrt(v2 - 1))
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
r = rx[numpy.logical_and(rx > psize, rx <= v * psize)]
|
|
161
|
+
r2 = r * r
|
|
162
|
+
f2 = (
|
|
163
|
+
1
|
|
164
|
+
- 3 * r / (4 * d * v) * (1 - r2 / (4 * d2) * (1 + 2.0 / (3 * v2)))
|
|
165
|
+
- 3.0
|
|
166
|
+
/ 8
|
|
167
|
+
* (1 + r2 / (2 * d2))
|
|
168
|
+
* sqrt(1 - d2 / r2)
|
|
169
|
+
* v
|
|
170
|
+
/ sqrt(v2 - 1)
|
|
171
|
+
- 3
|
|
172
|
+
* r
|
|
173
|
+
/ (4 * d)
|
|
174
|
+
* (1 - r2 / (4 * d2))
|
|
175
|
+
* v
|
|
176
|
+
/ sqrt(v2 - 1)
|
|
177
|
+
* (atan(sqrt(v2 - 1)) - atan(sqrt(r2 / d2 - 1)))
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
r = rx[rx > v * psize]
|
|
181
|
+
f3 = numpy.zeros_like(r)
|
|
182
|
+
|
|
183
|
+
f = numpy.concatenate((f1, f2, f3))
|
|
184
|
+
|
|
185
|
+
return f
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def lognormalSphericalCF(r, psize, psig):
|
|
189
|
+
"""Spherical nanoparticle characteristic function with lognormal size
|
|
190
|
+
distribution.
|
|
191
|
+
|
|
192
|
+
r -- distance of interaction
|
|
193
|
+
psize -- The mean particle diameter
|
|
194
|
+
psig -- The log-normal width of the particle diameter
|
|
195
|
+
|
|
196
|
+
Here, r is the independent variable, mu is the mean of the distribution
|
|
197
|
+
(not of the particle size), and s is the width of the distribution. This is
|
|
198
|
+
the characteristic function for the lognormal distribution of particle
|
|
199
|
+
diameter:
|
|
200
|
+
|
|
201
|
+
F(r, mu, s) = 0.5*Erfc((-mu-3*s^2+Log(r))/(sqrt(2)*s))
|
|
202
|
+
+ 0.25*r^3*Erfc((-mu+Log(r))/(sqrt(2)*s))*exp(-3*mu-4.5*s^2)
|
|
203
|
+
- 0.75*r*Erfc((-mu-2*s^2+Log(r))/(sqrt(2)*s))*exp(-mu-2.5*s^2)
|
|
204
|
+
|
|
205
|
+
The expectation value of the distribution gives the average particle
|
|
206
|
+
diameter, psize. The variance of the distribution gives psig^2. mu and s
|
|
207
|
+
can be expressed in terms of these as:
|
|
208
|
+
|
|
209
|
+
s^2 = log((psig/psize)^2 + 1)
|
|
210
|
+
mu = log(psize) - s^2/2
|
|
211
|
+
|
|
212
|
+
Source unknown
|
|
213
|
+
"""
|
|
214
|
+
if psize <= 0:
|
|
215
|
+
return numpy.zeros_like(r)
|
|
216
|
+
if psig <= 0:
|
|
217
|
+
return sphericalCF(r, psize)
|
|
218
|
+
|
|
219
|
+
sqrt2 = sqrt(2.0)
|
|
220
|
+
s = sqrt(log(psig * psig / (1.0 * psize * psize) + 1))
|
|
221
|
+
mu = log(psize) - s * s / 2
|
|
222
|
+
if mu < 0:
|
|
223
|
+
return numpy.zeros_like(r)
|
|
224
|
+
|
|
225
|
+
return (
|
|
226
|
+
0.5 * erfc((-mu - 3 * s * s + log(r)) / (sqrt2 * s))
|
|
227
|
+
+ 0.25
|
|
228
|
+
* r
|
|
229
|
+
* r
|
|
230
|
+
* r
|
|
231
|
+
* erfc((-mu + log(r)) / (sqrt2 * s))
|
|
232
|
+
* exp(-3 * mu - 4.5 * s * s)
|
|
233
|
+
- 0.75
|
|
234
|
+
* r
|
|
235
|
+
* erfc((-mu - 2 * s * s + log(r)) / (sqrt2 * s))
|
|
236
|
+
* exp(-mu - 2.5 * s * s)
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def sheetCF(r, sthick):
|
|
241
|
+
"""Nanosheet characteristic function.
|
|
242
|
+
|
|
243
|
+
r -- distance of interaction
|
|
244
|
+
sthick -- Thickness of nanosheet
|
|
245
|
+
|
|
246
|
+
From Kodama et al., Acta Cryst. A, 62, 444-453
|
|
247
|
+
"""
|
|
248
|
+
# handle zero or negative sthick. make it work for scalars and arrays.
|
|
249
|
+
if sthick <= 0:
|
|
250
|
+
return 0 * sthick
|
|
251
|
+
# process scalar r
|
|
252
|
+
if numpy.isscalar(r):
|
|
253
|
+
rv = 1 - 0.5 * r / sthick if r < sthick else 0.5 * sthick / r
|
|
254
|
+
return rv
|
|
255
|
+
# handle array-type r
|
|
256
|
+
ra = numpy.asarray(r)
|
|
257
|
+
lo = ra < sthick
|
|
258
|
+
hi = ~lo
|
|
259
|
+
f = numpy.empty_like(ra, dtype=float)
|
|
260
|
+
f[lo] = 1 - 0.5 * ra[lo] / sthick
|
|
261
|
+
f[hi] = 0.5 * sthick / ra[hi]
|
|
262
|
+
return f
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def shellCF(r, radius, thickness):
|
|
266
|
+
"""Spherical shell characteristic function.
|
|
267
|
+
|
|
268
|
+
radius -- Inner radius
|
|
269
|
+
thickness -- Thickness of shell
|
|
270
|
+
|
|
271
|
+
outer radius = radius + thickness
|
|
272
|
+
|
|
273
|
+
From Lei et al., Phys. Rev. B, 80, 024118 (2009)
|
|
274
|
+
"""
|
|
275
|
+
d = 1.0 * thickness
|
|
276
|
+
a = 1.0 * radius + d / 2.0
|
|
277
|
+
return shellCF2(r, a, d)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def shellCF2(r, a, delta):
|
|
281
|
+
"""Spherical shell characteristic function.
|
|
282
|
+
|
|
283
|
+
a -- Central radius
|
|
284
|
+
delta -- Thickness of shell
|
|
285
|
+
|
|
286
|
+
outer radius = a + thickness/2
|
|
287
|
+
|
|
288
|
+
From Lei et al., Phys. Rev. B, 80, 024118 (2009)
|
|
289
|
+
"""
|
|
290
|
+
a = 1.0 * a
|
|
291
|
+
d = 1.0 * delta
|
|
292
|
+
a2 = a**2
|
|
293
|
+
d2 = d**2
|
|
294
|
+
dmr = d - r
|
|
295
|
+
dmr2 = dmr**2
|
|
296
|
+
|
|
297
|
+
f = (
|
|
298
|
+
r
|
|
299
|
+
* (
|
|
300
|
+
16 * a * a2
|
|
301
|
+
+ 12 * a * d * dmr
|
|
302
|
+
+ 36 * a2 * (2 * d - r)
|
|
303
|
+
+ 3 * dmr2 * (2 * d + r)
|
|
304
|
+
)
|
|
305
|
+
+ 2 * dmr2 * (r * (2 * d + r) - 12 * a2) * sign(dmr)
|
|
306
|
+
- 2 * (2 * a - r) ** 2 * (r * (4 * a + r) - 3 * d2) * sign(2 * a - r)
|
|
307
|
+
+ r * (4 * a - 2 * d + r) * (2 * a - d - r) ** 2 * sign(2 * a - d - r)
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
f[r > 2 * a + d] = 0
|
|
311
|
+
|
|
312
|
+
den = 8.0 * r * d * (12 * a2 + d2)
|
|
313
|
+
zmask = den == 0.0
|
|
314
|
+
vmask = ~zmask
|
|
315
|
+
f[vmask] /= den[vmask]
|
|
316
|
+
f[zmask] = 1
|
|
317
|
+
return f
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class SASCF(Calculator):
|
|
321
|
+
"""Calculator class for characteristic functions from sas-models.
|
|
322
|
+
|
|
323
|
+
This class wraps a sas.models.BaseModel to calculate I(Q) related to
|
|
324
|
+
nanoparticle shape. This I(Q) is inverted to f(r) according to:
|
|
325
|
+
f(r) = 1 / (4 pi r) * SINFT(I(Q)),
|
|
326
|
+
where "SINFT" represents the sine Fourier transform.
|
|
327
|
+
|
|
328
|
+
Attributes:
|
|
329
|
+
_model -- BaseModel object this adapts.
|
|
330
|
+
|
|
331
|
+
Managed Parameters:
|
|
332
|
+
These depend on the parameters of the BaseModel object held by _model. They
|
|
333
|
+
are created from the 'params' attribute of the BaseModel. If a dispersion
|
|
334
|
+
is set for the BaseModel, the dispersion "width" will be accessible under
|
|
335
|
+
"<parname>_width", where <parname> is the name a parameter adjusted by
|
|
336
|
+
dispersion.
|
|
337
|
+
"""
|
|
338
|
+
|
|
339
|
+
def __init__(self, name, model):
|
|
340
|
+
"""Initialize the generator.
|
|
341
|
+
|
|
342
|
+
name -- A name for the SASCF
|
|
343
|
+
model -- SASModel object this adapts.
|
|
344
|
+
"""
|
|
345
|
+
Calculator.__init__(self, name)
|
|
346
|
+
|
|
347
|
+
self._model = model
|
|
348
|
+
|
|
349
|
+
from diffpy.srfit.sas.sasparameter import SASParameter
|
|
350
|
+
|
|
351
|
+
# Wrap normal parameters
|
|
352
|
+
for parname in model.params:
|
|
353
|
+
par = SASParameter(parname, model)
|
|
354
|
+
self.addParameter(par)
|
|
355
|
+
|
|
356
|
+
# Wrap dispersion parameters
|
|
357
|
+
for parname in model.dispersion:
|
|
358
|
+
name = parname + "_width"
|
|
359
|
+
parname += ".width"
|
|
360
|
+
par = SASParameter(name, model, parname)
|
|
361
|
+
self.addParameter(par)
|
|
362
|
+
|
|
363
|
+
return
|
|
364
|
+
|
|
365
|
+
def __call__(self, r):
|
|
366
|
+
"""Calculate the characteristic function from the transform of the
|
|
367
|
+
BaseModel."""
|
|
368
|
+
|
|
369
|
+
# Determine q-values.
|
|
370
|
+
# We want very fine r-spacing so we can properly normalize f(r). This
|
|
371
|
+
# equates to having a large qmax so that the Fourier transform is
|
|
372
|
+
# finely spaced. We also want the calculation to be fast, so we pick
|
|
373
|
+
# qmax such that the number of q-points is a power of 2. This allows us
|
|
374
|
+
# to use the fft.
|
|
375
|
+
#
|
|
376
|
+
# The initial dr is somewhat arbitrary, but using dr = 0.01 allows for
|
|
377
|
+
# the f(r) calculated from a particle of diameter 50, over r =
|
|
378
|
+
# arange(1, 60, 0.1) to agree with the sphericalCF with Rw < 1e-4%.
|
|
379
|
+
#
|
|
380
|
+
# We also have to make a q-spacing small enough to compute out to at
|
|
381
|
+
# least the size of the signal.
|
|
382
|
+
dr = min(0.01, r[1] - r[0])
|
|
383
|
+
ed = 2 * self._model.calculate_ER()
|
|
384
|
+
|
|
385
|
+
# Check for nans. If we find any, then return zeros.
|
|
386
|
+
if numpy.isnan(ed).any():
|
|
387
|
+
y = numpy.zeros_like(r)
|
|
388
|
+
return y
|
|
389
|
+
|
|
390
|
+
rmax = max(ed, 2 * r[-1])
|
|
391
|
+
dq = pi / rmax
|
|
392
|
+
qmax = pi / dr
|
|
393
|
+
numpoints = int(2 ** (ceil(log2(qmax / dq))))
|
|
394
|
+
qmax = dq * numpoints
|
|
395
|
+
|
|
396
|
+
# Calculate F(q) = q * I(q) from model
|
|
397
|
+
q = fftfreq(int(qmax / dq)) * qmax
|
|
398
|
+
fq = q * self._model.evalDistribution(q)
|
|
399
|
+
|
|
400
|
+
# Calculate g(r) and the effective r-points
|
|
401
|
+
rp = fftfreq(numpoints) * 2 * pi / dq
|
|
402
|
+
# Note sine transform = imaginary part of ifft
|
|
403
|
+
gr = ifft(fq).imag
|
|
404
|
+
|
|
405
|
+
# Calculate full-fr for normalization
|
|
406
|
+
assert rp[0] == 0.0
|
|
407
|
+
frp = numpy.zeros_like(gr)
|
|
408
|
+
frp[1:] = gr[1:] / rp[1:]
|
|
409
|
+
|
|
410
|
+
# Inerpolate onto requested grid, do not use data after jump in rp
|
|
411
|
+
assert numpoints % 2 == 0
|
|
412
|
+
nhalf = numpoints / 2
|
|
413
|
+
fr = numpy.interp(r, rp[:nhalf], gr[:nhalf])
|
|
414
|
+
vmask = r != 0
|
|
415
|
+
fr[vmask] /= r[vmask]
|
|
416
|
+
|
|
417
|
+
# Normalize. We approximate fr[0] by using the fact that f(r) is linear
|
|
418
|
+
# at low r. By definition, fr[0] should equal 1.
|
|
419
|
+
fr0 = 2 * frp[2] - frp[1]
|
|
420
|
+
fr /= fr0
|
|
421
|
+
|
|
422
|
+
# Fix potential divide-by-zero issue, fr is 1 at r == 0
|
|
423
|
+
fr[~vmask] = 1
|
|
424
|
+
|
|
425
|
+
return fr
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def erfc(x):
|
|
429
|
+
return 1.0 - erf(x)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
# End of file
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##############################################################################
|
|
3
|
+
#
|
|
4
|
+
# diffpy.srfit by DANSE Diffraction group
|
|
5
|
+
# Simon J. L. Billinge
|
|
6
|
+
# (c) 2008 The Trustees of Columbia University
|
|
7
|
+
# in the City of New York. All rights reserved.
|
|
8
|
+
#
|
|
9
|
+
# File coded by: Chris Farrow
|
|
10
|
+
#
|
|
11
|
+
# See AUTHORS.txt for a list of people who contributed.
|
|
12
|
+
# See LICENSE_DANSE.txt for license information.
|
|
13
|
+
#
|
|
14
|
+
##############################################################################
|
|
15
|
+
"""PDF profile generator using the Debye equation.
|
|
16
|
+
|
|
17
|
+
The DebyePDFGenerator class can take a diffpy.structure,
|
|
18
|
+
pyobjcryst.crystal.Crystal or pyobjcryst.molecule.Molecule object and
|
|
19
|
+
calculate the PDF from it. This generator is especially appropriate for
|
|
20
|
+
isolated scatterers, such as nanoparticles and molecules.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
__all__ = ["DebyePDFGenerator"]
|
|
24
|
+
|
|
25
|
+
from diffpy.srfit.pdf.basepdfgenerator import BasePDFGenerator
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DebyePDFGenerator(BasePDFGenerator):
|
|
29
|
+
"""A class for calculating the PDF from an isolated scatterer.
|
|
30
|
+
|
|
31
|
+
This works with diffpy.structure.Structure, pyobjcryst.crystal.Crystal and
|
|
32
|
+
pyobjcryst.molecule.Molecule instances. Note that the managed Parameters
|
|
33
|
+
are not created until the structure is added.
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
_calc -- DebyePDFCalculator instance for calculating the PDF
|
|
37
|
+
_phase -- The structure ParameterSets used to calculate the profile.
|
|
38
|
+
stru -- The structure objected adapted by _phase.
|
|
39
|
+
_lastr -- The last value of r over which the PDF was calculated. This is
|
|
40
|
+
used to configure the calculator when r changes.
|
|
41
|
+
|
|
42
|
+
Managed Parameters:
|
|
43
|
+
scale -- Scale factor
|
|
44
|
+
delta1 -- Linear peak broadening term
|
|
45
|
+
delta2 -- Quadratic peak broadening term
|
|
46
|
+
qbroad -- Resolution peak broadening term
|
|
47
|
+
qdamp -- Resolution peak dampening term
|
|
48
|
+
|
|
49
|
+
Managed ParameterSets:
|
|
50
|
+
The structure ParameterSet (SrRealStructure instance) used to calculate the
|
|
51
|
+
profile is named by the user.
|
|
52
|
+
|
|
53
|
+
Usable Metadata:
|
|
54
|
+
stype -- The scattering type "X" for x-ray, "N" for neutron (see
|
|
55
|
+
'setScatteringType').
|
|
56
|
+
qmax -- The maximum scattering vector used to generate the PDF (see
|
|
57
|
+
setQmax).
|
|
58
|
+
qmin -- The minimum scattering vector used to generate the PDF (see
|
|
59
|
+
setQmin).
|
|
60
|
+
scale -- See Managed Parameters.
|
|
61
|
+
delta1 -- See Managed Parameters.
|
|
62
|
+
delta2 -- See Managed Parameters.
|
|
63
|
+
qbroad -- See Managed Parameters.
|
|
64
|
+
qdamp -- See Managed Parameters.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def setStructure(self, stru, name="phase", periodic=False):
|
|
68
|
+
"""Set the structure that will be used to calculate the PDF.
|
|
69
|
+
|
|
70
|
+
This creates a DiffpyStructureParSet, ObjCrystCrystalParSet or
|
|
71
|
+
ObjCrystMoleculeParSet that adapts stru to a ParameterSet interface.
|
|
72
|
+
See those classes (located in diffpy.srfit.structure) for how they are
|
|
73
|
+
used. The resulting ParameterSet will be managed by this generator.
|
|
74
|
+
|
|
75
|
+
stru -- diffpy.structure.Structure, pyobjcryst.crystal.Crystal or
|
|
76
|
+
pyobjcryst.molecule.Molecule instance. Default None.
|
|
77
|
+
name -- A name to give to the managed ParameterSet that adapts stru
|
|
78
|
+
(default "phase").
|
|
79
|
+
periodic -- The structure should be treated as periodic (default
|
|
80
|
+
False). Note that some structures do not support
|
|
81
|
+
periodicity, in which case this will have no effect on the
|
|
82
|
+
PDF calculation.
|
|
83
|
+
"""
|
|
84
|
+
return BasePDFGenerator.setStructure(self, stru, name, periodic)
|
|
85
|
+
|
|
86
|
+
def setPhase(self, parset, periodic=False):
|
|
87
|
+
"""Set the phase that will be used to calculate the PDF.
|
|
88
|
+
|
|
89
|
+
Set the phase directly with a DiffpyStructureParSet,
|
|
90
|
+
ObjCrystCrystalParSet or ObjCrystMoleculeParSet that adapts a structure
|
|
91
|
+
object (from diffpy or pyobjcryst). The passed ParameterSet will be
|
|
92
|
+
managed by this generator.
|
|
93
|
+
|
|
94
|
+
parset -- A SrRealParSet that holds the structural information.
|
|
95
|
+
This can be used to share the phase between multiple
|
|
96
|
+
BasePDFGenerators, and have the changes in one reflect in
|
|
97
|
+
another.
|
|
98
|
+
periodic -- The structure should be treated as periodic (default True).
|
|
99
|
+
Note that some structures do not support periodicity, in
|
|
100
|
+
which case this will be ignored.
|
|
101
|
+
"""
|
|
102
|
+
return BasePDFGenerator.setPhase(self, parset, periodic)
|
|
103
|
+
|
|
104
|
+
def __init__(self, name="pdf"):
|
|
105
|
+
"""Initialize the generator."""
|
|
106
|
+
from diffpy.srreal.pdfcalculator import DebyePDFCalculator
|
|
107
|
+
|
|
108
|
+
BasePDFGenerator.__init__(self, name)
|
|
109
|
+
self._setCalculator(DebyePDFCalculator())
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# End class DebyePDFGenerator
|
|
114
|
+
|
|
115
|
+
# End of file
|