feffdb 0.1__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.
- feffdb/__init__.py +7 -0
- feffdb/apps.py +77 -0
- feffdb/feffdat.db +0 -0
- feffdb/feffdb.py +318 -0
- feffdb/feffpath.py +559 -0
- feffdb/schema.py +80 -0
- feffdb/simpledb.py +373 -0
- feffdb/utils.py +359 -0
- feffdb/version.py +24 -0
- feffdb-0.1.dist-info/METADATA +62 -0
- feffdb-0.1.dist-info/RECORD +15 -0
- feffdb-0.1.dist-info/WHEEL +5 -0
- feffdb-0.1.dist-info/entry_points.txt +2 -0
- feffdb-0.1.dist-info/licenses/LICENSE +28 -0
- feffdb-0.1.dist-info/top_level.txt +1 -0
feffdb/feffpath.py
ADDED
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
"""
|
|
2
|
+
feffpath provides a simple function to read and use the Feff data
|
|
3
|
+
results in FeffDB. This is a slightly simplified version of the code
|
|
4
|
+
to work with Feff data files in X-ray Larch, and is included here to
|
|
5
|
+
give a standalone for to use the data in FeffDB.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
path1 = read_feffdat('feffNNNN.dat')
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
import re
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
import hashlib
|
|
18
|
+
from base64 import b64encode
|
|
19
|
+
from scipy import constants as consts
|
|
20
|
+
from scipy.interpolate import UnivariateSpline
|
|
21
|
+
|
|
22
|
+
from xraydb import atomic_mass, atomic_symbol
|
|
23
|
+
from pyshortcuts import read_textfile, gformat
|
|
24
|
+
|
|
25
|
+
from .feffdb import FeffDatabase
|
|
26
|
+
|
|
27
|
+
KTOE = 1.e20*consts.hbar**2 / (2*consts.m_e * consts.e)
|
|
28
|
+
ETOK = 1.0/KTOE
|
|
29
|
+
|
|
30
|
+
SMALL_ENERGY = 1.e-6
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
FDAT_ARRS = ('real_phc', 'mag_feff', 'pha_feff', 'red_fact',
|
|
34
|
+
'lam', 'rep', 'pha', 'amp', 'k')
|
|
35
|
+
|
|
36
|
+
# values that will be available in calculations of Path Parameter values
|
|
37
|
+
FEFFDAT_VALUES = ('reff', 'nleg', 'degen', 'rmass', 'rnorman',
|
|
38
|
+
'gam_ch', 'rs_int', 'vint', 'vmu', 'vfermi')
|
|
39
|
+
|
|
40
|
+
FEFF_DB = None
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class Group:
|
|
44
|
+
"""Class for keeping track of an item in inventory."""
|
|
45
|
+
name: str
|
|
46
|
+
unit_price: float
|
|
47
|
+
quantity_on_hand: int = 0
|
|
48
|
+
|
|
49
|
+
def total_cost(self) -> float:
|
|
50
|
+
return self.unit_price * self.quantity_on_hand
|
|
51
|
+
|
|
52
|
+
def gfmt(x, n=10):
|
|
53
|
+
return gformat(x, length=n)
|
|
54
|
+
|
|
55
|
+
def etok(energy):
|
|
56
|
+
"""convert photo-electron energy to wavenumber"""
|
|
57
|
+
if energy < 0: return 0
|
|
58
|
+
return np.sqrt(energy*ETOK)
|
|
59
|
+
|
|
60
|
+
def ktoe(k):
|
|
61
|
+
"""convert photo-electron wavenumber to energy"""
|
|
62
|
+
return k*k*KTOE
|
|
63
|
+
|
|
64
|
+
def b64hash(val):
|
|
65
|
+
"""return a base32 hash of a string"""
|
|
66
|
+
_hash = hashlib.sha256()
|
|
67
|
+
_hash.update(val.encode('utf-8'))
|
|
68
|
+
return b64encode(_hash.digest()).decode('utf-8')
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def pathname_valid(fname):
|
|
72
|
+
if fname in ('', None):
|
|
73
|
+
return False
|
|
74
|
+
return (Path(fname).exists() or
|
|
75
|
+
(fname.startswith('feffdb[') and fname.endswith(']')))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class FeffDatFile:
|
|
79
|
+
def __init__(self, filename=None):
|
|
80
|
+
self.filename = filename
|
|
81
|
+
self.__reff__ = 0
|
|
82
|
+
self.__nleg__ = 0
|
|
83
|
+
self.__rmass = None
|
|
84
|
+
self.__geometry = None
|
|
85
|
+
if pathname_valid(filename):
|
|
86
|
+
self._read(filename)
|
|
87
|
+
|
|
88
|
+
def __repr__(self):
|
|
89
|
+
if self.filename is not None:
|
|
90
|
+
return f'FeffDatFile({self.filename})'
|
|
91
|
+
return 'FeffDatFile()'
|
|
92
|
+
|
|
93
|
+
def __copy__(self):
|
|
94
|
+
return FeffDatFile(filename=self.filename)
|
|
95
|
+
|
|
96
|
+
def __deepcopy__(self, memo):
|
|
97
|
+
return FeffDatFile(filename=self.filename)
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def reff(self):
|
|
101
|
+
return self.__reff__
|
|
102
|
+
|
|
103
|
+
@reff.setter
|
|
104
|
+
def reff(self, val):
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def nleg(self):
|
|
109
|
+
return self.__nleg__
|
|
110
|
+
|
|
111
|
+
@nleg.setter
|
|
112
|
+
def nleg(self, val):
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def rmass(self):
|
|
117
|
+
"""reduced mass for a path"""
|
|
118
|
+
if self.__rmass is None and hasattr(self, 'geom'):
|
|
119
|
+
rmass = 0
|
|
120
|
+
for atsym, iz, ipot, amass, x, y, z in self.geom:
|
|
121
|
+
rmass += 1.0/max(1., amass)
|
|
122
|
+
self.__rmass = 1./rmass
|
|
123
|
+
return self.__rmass
|
|
124
|
+
|
|
125
|
+
@rmass.setter
|
|
126
|
+
def rmass(self, val):
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def geometry(self):
|
|
131
|
+
"""path geometry -- more complete than 'geom', closing matching `geometry`
|
|
132
|
+
from feffutils and get_feff_pathinfo, except without 'eta'
|
|
133
|
+
"""
|
|
134
|
+
if not hasattr(self, '_geometry'):
|
|
135
|
+
self.__geometry = None
|
|
136
|
+
if not hasattr(self, 'geom'):
|
|
137
|
+
return None
|
|
138
|
+
if self.__geometry is None:
|
|
139
|
+
self.__geometry = []
|
|
140
|
+
pts, atoms = [], []
|
|
141
|
+
for atsym, iz, ipot, amass, x, y, z in self.geom:
|
|
142
|
+
pts.append(np.array([float(x), float(y), float(z)]))
|
|
143
|
+
atoms.append([atsym, ipot, float(x), float(y), float(z)])
|
|
144
|
+
pts.append(pts[0])
|
|
145
|
+
pts.append(pts[1])
|
|
146
|
+
atoms.append(atoms[0])
|
|
147
|
+
atoms.append(atoms[1])
|
|
148
|
+
|
|
149
|
+
for i in range(1, len(pts)-1):
|
|
150
|
+
p0 = pts[i-1]
|
|
151
|
+
p1 = pts[i+0]
|
|
152
|
+
p2 = pts[i+1]
|
|
153
|
+
a = p1 - p0
|
|
154
|
+
b = p2 - p1
|
|
155
|
+
dist = float(np.linalg.norm(pts[i]-pts[i-1]))
|
|
156
|
+
beta = float(np.acos(np.dot(a, b) / np.sqrt((np.dot(a, a) * np.dot(b, b))))*180/np.pi)
|
|
157
|
+
sym, ipot, x, y, z = atoms[i]
|
|
158
|
+
self.__geometry.append((sym, ipot, dist, x, y, z, beta, 0.0))
|
|
159
|
+
return self.__geometry
|
|
160
|
+
|
|
161
|
+
@geometry.setter
|
|
162
|
+
def geometry(self, val):
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
def _set_from_dict(self, **kws):
|
|
166
|
+
self.__rmass = None
|
|
167
|
+
self.__geometry = None
|
|
168
|
+
for key, val in kws.items():
|
|
169
|
+
if key == 'rmass':
|
|
170
|
+
continue
|
|
171
|
+
elif key == 'reff':
|
|
172
|
+
key = '__reff__'
|
|
173
|
+
elif key == 'nleg':
|
|
174
|
+
key = '__nleg__'
|
|
175
|
+
elif key in FDAT_ARRS:
|
|
176
|
+
val = np.array(val)
|
|
177
|
+
setattr(self, key, val)
|
|
178
|
+
|
|
179
|
+
def __setstate__(self, state):
|
|
180
|
+
(self.filename, self.title, self.version, self.shell,
|
|
181
|
+
self.absorber, self.degen, self.__reff__, self.__nleg__,
|
|
182
|
+
self.rnorman, self.edge, self.gam_ch, self.exch, self.vmu, self.vfermi,
|
|
183
|
+
self.vint, self.rs_int, self.potentials, self.geom, self.__rmass,
|
|
184
|
+
self.k, self.real_phc, self.mag_feff, self.pha_feff,
|
|
185
|
+
self.red_fact, self.lam, self.rep, self.pha, self.amp) = state
|
|
186
|
+
self.k = np.array(self.k)
|
|
187
|
+
self.real_phc = np.array(self.real_phc)
|
|
188
|
+
self.mag_feff = np.array(self.mag_feff)
|
|
189
|
+
self.pha_feff = np.array(self.pha_feff)
|
|
190
|
+
self.red_fact = np.array(self.red_fact)
|
|
191
|
+
self.lam = np.array(self.lam)
|
|
192
|
+
self.rep = np.array(self.rep)
|
|
193
|
+
self.pha = np.array(self.pha)
|
|
194
|
+
self.amp = np.array(self.amp)
|
|
195
|
+
|
|
196
|
+
def __getstate__(self):
|
|
197
|
+
return (self.filename, self.title, self.version, self.shell,
|
|
198
|
+
self.absorber, self.degen, self.__reff__, self.__nleg__,
|
|
199
|
+
self.rnorman, self.edge, self.gam_ch, self.exch, self.vmu,
|
|
200
|
+
self.vfermi, self.vint, self.rs_int, self.potentials,
|
|
201
|
+
self.geom, self.__rmass, self.k.tolist(),
|
|
202
|
+
self.real_phc.tolist(), self.mag_feff.tolist(),
|
|
203
|
+
self.pha_feff.tolist(), self.red_fact.tolist(),
|
|
204
|
+
self.lam.tolist(), self.rep.tolist(), self.pha.tolist(),
|
|
205
|
+
self.amp.tolist())
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _read(self, filename):
|
|
209
|
+
if Path(filename).exists():
|
|
210
|
+
fefftext = read_textfile(filename)
|
|
211
|
+
elif (filename.startswith('feffdb[') and filename.endswith(']')):
|
|
212
|
+
global FEFF_DB
|
|
213
|
+
if FEFF_DB is None and FeffDatabase is not None:
|
|
214
|
+
FEFF_DB = FeffDatabase()
|
|
215
|
+
try:
|
|
216
|
+
fefftext = FEFF_DB.get_feffdat(filename[7:-1])
|
|
217
|
+
except Exception:
|
|
218
|
+
print(f"Error reading Feff Database '{filename}'")
|
|
219
|
+
return
|
|
220
|
+
if fefftext is None:
|
|
221
|
+
print(f"could not find entry in Feff Database '{filename}'")
|
|
222
|
+
return
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
self.filename = filename
|
|
226
|
+
mode = 'header'
|
|
227
|
+
self.potentials, self.geom = [], []
|
|
228
|
+
data = []
|
|
229
|
+
pcounter = 0
|
|
230
|
+
iline = 0
|
|
231
|
+
for line in fefftext.split('\n'):
|
|
232
|
+
iline += 1
|
|
233
|
+
line = line.strip()
|
|
234
|
+
if line.startswith('#'): line = line[1:]
|
|
235
|
+
line = line.strip()
|
|
236
|
+
if iline == 1:
|
|
237
|
+
self.title = line[:64].strip()
|
|
238
|
+
self.version = line[64:].strip()
|
|
239
|
+
continue
|
|
240
|
+
if line.startswith('k') and line.endswith('real[p]@#'):
|
|
241
|
+
mode = 'arrays'
|
|
242
|
+
continue
|
|
243
|
+
elif '----' in line[2:10]:
|
|
244
|
+
mode = 'path'
|
|
245
|
+
continue
|
|
246
|
+
#
|
|
247
|
+
if (mode == 'header' and
|
|
248
|
+
(re.match(r'^Abs\b', line) or re.match(r'^Pot\s+\d+\b', line)) and
|
|
249
|
+
re.search(r'\bZ\s*=', line)):
|
|
250
|
+
words = line.replace('=', ' ').split()
|
|
251
|
+
ipot, z, rmt, rnm = (0, 0, 0, 0)
|
|
252
|
+
words.pop(0)
|
|
253
|
+
if re.match(r'^Pot\s+\d+\b', line):
|
|
254
|
+
ipot = int(words.pop(0))
|
|
255
|
+
iz = int(words[1])
|
|
256
|
+
rmt = float(words[3])
|
|
257
|
+
rnm = float(words[5])
|
|
258
|
+
if re.match(r'^Abs\b', line):
|
|
259
|
+
self.shell = words[6]
|
|
260
|
+
self.potentials.append((ipot, iz, rmt, rnm))
|
|
261
|
+
elif mode == 'header' and re.match(r'^Gam_ch\s*=', line):
|
|
262
|
+
words = line.replace('=', ' ').split(None, 2)
|
|
263
|
+
self.gam_ch = float(words[1])
|
|
264
|
+
self.exch = words[2]
|
|
265
|
+
elif mode == 'header' and re.match(r'^Mu\s*=', line):
|
|
266
|
+
words = line.replace('=', ' ').replace('eV', ' ').split()
|
|
267
|
+
self.vmu = float(words[1])
|
|
268
|
+
self.vfermi = ktoe(float(words[3]))
|
|
269
|
+
self.vint = float(words[5])
|
|
270
|
+
self.rs_int= float(words[7])
|
|
271
|
+
elif mode == 'path':
|
|
272
|
+
pcounter += 1
|
|
273
|
+
if pcounter == 1:
|
|
274
|
+
w = [float(x) for x in line.split()[:5]]
|
|
275
|
+
self.__nleg__ = int(w.pop(0))
|
|
276
|
+
self.degen, self.__reff__, self.rnorman, self.edge = w
|
|
277
|
+
elif pcounter > 2:
|
|
278
|
+
words = line.split()
|
|
279
|
+
xyz = ["%8.5f" % float(x) for x in words[:3]]
|
|
280
|
+
ipot = int(words[3])
|
|
281
|
+
iz = int(words[4])
|
|
282
|
+
if len(words) > 5:
|
|
283
|
+
lab = words[5]
|
|
284
|
+
else:
|
|
285
|
+
lab = atomic_symbol(iz)
|
|
286
|
+
amass = atomic_mass(iz)
|
|
287
|
+
geom = [lab, iz, ipot, amass] + xyz
|
|
288
|
+
if len(self.geom) == 0:
|
|
289
|
+
self.absorber = lab
|
|
290
|
+
self.geom.append(tuple(geom))
|
|
291
|
+
elif mode == 'arrays':
|
|
292
|
+
d = np.array([float(x) for x in line.split()])
|
|
293
|
+
if len(d) == 7:
|
|
294
|
+
data.append(d)
|
|
295
|
+
data = np.array(data).transpose()
|
|
296
|
+
self.k = data[0]
|
|
297
|
+
self.real_phc = data[1]
|
|
298
|
+
self.mag_feff = data[2]
|
|
299
|
+
self.pha_feff = data[3]
|
|
300
|
+
self.red_fact = data[4]
|
|
301
|
+
self.lam = data[5]
|
|
302
|
+
self.rep = data[6]
|
|
303
|
+
self.pha = data[1] + data[3]
|
|
304
|
+
self.amp = data[2] * data[4]
|
|
305
|
+
self.__rmass = None # reduced mass of path
|
|
306
|
+
self.__geometry = None # path geometry list [atom, dist, angle]
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class FeffPath():
|
|
311
|
+
"""FeffPath object
|
|
312
|
+
has a FeffDatFile and can calculate EXAFS with its `calc_chi` method.
|
|
313
|
+
"""
|
|
314
|
+
def __init__(self, filename=None, label='', feffrun='', s02=None, degen=None,
|
|
315
|
+
e0=None, ei=None, deltar=None, sigma2=None, third=None,
|
|
316
|
+
fourth=None):
|
|
317
|
+
self.filename = filename
|
|
318
|
+
self.feffrun = feffrun
|
|
319
|
+
self.label = label
|
|
320
|
+
self.params = None
|
|
321
|
+
self.spline_coefs = None
|
|
322
|
+
self.geom = []
|
|
323
|
+
self.shell = 'K'
|
|
324
|
+
self.absorber = None
|
|
325
|
+
self.dataset = None
|
|
326
|
+
self.hashkey = None
|
|
327
|
+
self.k = None
|
|
328
|
+
self.chi = None
|
|
329
|
+
self.use = True
|
|
330
|
+
|
|
331
|
+
self.degen = 1.0 if degen is None else degen
|
|
332
|
+
self.s02 = 1.0 if s02 is None else s02
|
|
333
|
+
self.e0 = 0.0 if e0 is None else e0
|
|
334
|
+
self.ei = 0.0 if ei is None else ei
|
|
335
|
+
self.deltar = 0.0 if deltar is None else deltar
|
|
336
|
+
self.sigma2 = 0.0 if sigma2 is None else sigma2
|
|
337
|
+
self.third = 0.0 if third is None else third
|
|
338
|
+
self.fourth = 0.0 if fourth is None else fourth
|
|
339
|
+
|
|
340
|
+
self._feffdat = None
|
|
341
|
+
if filename is not None:
|
|
342
|
+
self._get_feffdat(filename)
|
|
343
|
+
|
|
344
|
+
def _get_feffdat(self, filename):
|
|
345
|
+
if filename not in ('', None):
|
|
346
|
+
self._feffdat = FeffDatFile(filename=filename)
|
|
347
|
+
xpath = Path(filename).absolute()
|
|
348
|
+
self.filename = xpath.as_posix()
|
|
349
|
+
self.feffrun = xpath.parent.as_posix()
|
|
350
|
+
|
|
351
|
+
self.create_spline_coefs()
|
|
352
|
+
self.geom = self._feffdat.geom
|
|
353
|
+
self.shell = self._feffdat.shell
|
|
354
|
+
self.absorber = self._feffdat.absorber
|
|
355
|
+
|
|
356
|
+
self.hashkey = self.__geom2label()
|
|
357
|
+
if self.label in ('', None):
|
|
358
|
+
self.label = self.hashkey
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def __getstate__(self):
|
|
362
|
+
_feffdat_state = self._feffdat.__getstate__()
|
|
363
|
+
return (self.filename, self.label, self.feffrun, self.degen,
|
|
364
|
+
self.s02, self.e0, self.ei, self.deltar, self.sigma2,
|
|
365
|
+
self.third, self.fourth, self.use, _feffdat_state)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def __setstate__(self, state):
|
|
369
|
+
self.params = self.spline_coefs = self.k = self.chi = None
|
|
370
|
+
|
|
371
|
+
(self.filename, self.label, self.feffrun, self.degen,
|
|
372
|
+
self.s02, self.e0, self.ei, self.deltar, self.sigma2,
|
|
373
|
+
self.third, self.fourth, self.use, _feffdat_state) = state
|
|
374
|
+
|
|
375
|
+
self._feffdat = FeffDatFile()
|
|
376
|
+
self._feffdat.__setstate__(_feffdat_state)
|
|
377
|
+
self.filename = Path(self.filename).absolute().as_posix()
|
|
378
|
+
|
|
379
|
+
self.create_spline_coefs()
|
|
380
|
+
|
|
381
|
+
self.geom = self._feffdat.geom
|
|
382
|
+
self.shell = self._feffdat.shell
|
|
383
|
+
self.absorber = self._feffdat.absorber
|
|
384
|
+
|
|
385
|
+
self.hashkey = self.__geom2label()
|
|
386
|
+
if self.label in ('', None):
|
|
387
|
+
self.label = self.hashkey
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def __geom2label(self):
|
|
391
|
+
"""generate label by hashing full filename and path geometry"""
|
|
392
|
+
rep = [self._feffdat.degen, self._feffdat.shell, self.filename]
|
|
393
|
+
for atom in self.geom:
|
|
394
|
+
rep.extend(atom)
|
|
395
|
+
rep.append("%7.4f" % self._feffdat.reff)
|
|
396
|
+
s = "|".join([str(i) for i in rep])
|
|
397
|
+
return "p%s" % (b64hash(s)[:8])
|
|
398
|
+
|
|
399
|
+
def pathpar_name(self, parname):
|
|
400
|
+
"""
|
|
401
|
+
get internal name of lmfit Parameter for a path paramter,
|
|
402
|
+
using Path's hashkey
|
|
403
|
+
"""
|
|
404
|
+
return f'{parname}_{self.dataset}_{self.hashkey}'
|
|
405
|
+
|
|
406
|
+
def __copy__(self):
|
|
407
|
+
newpath = FeffPath()
|
|
408
|
+
newpath.__setstate__(self.__getstate__())
|
|
409
|
+
return newpath
|
|
410
|
+
|
|
411
|
+
def __deepcopy__(self, memo):
|
|
412
|
+
newpath = FeffPath()
|
|
413
|
+
newpath.__setstate__(self.__getstate__())
|
|
414
|
+
return newpath
|
|
415
|
+
|
|
416
|
+
@property
|
|
417
|
+
def reff(self): return self._feffdat.reff
|
|
418
|
+
|
|
419
|
+
@reff.setter
|
|
420
|
+
def reff(self, val): pass
|
|
421
|
+
|
|
422
|
+
@property
|
|
423
|
+
def nleg(self): return self._feffdat.nleg
|
|
424
|
+
|
|
425
|
+
@nleg.setter
|
|
426
|
+
def nleg(self, val): pass
|
|
427
|
+
|
|
428
|
+
@property
|
|
429
|
+
def rmass(self): return self._feffdat.rmass
|
|
430
|
+
|
|
431
|
+
@rmass.setter
|
|
432
|
+
def rmass(self, val): pass
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
@property
|
|
436
|
+
def geometry(self):
|
|
437
|
+
return self._feffdat.geometry
|
|
438
|
+
|
|
439
|
+
@geometry.setter
|
|
440
|
+
def geometry(self, val):
|
|
441
|
+
pass
|
|
442
|
+
|
|
443
|
+
def __repr__(self):
|
|
444
|
+
return f"FeffPath(label='{self.label}', filename='{self.filename}')"
|
|
445
|
+
|
|
446
|
+
def create_spline_coefs(self):
|
|
447
|
+
"""pre-calculate spline coefficients for feff data"""
|
|
448
|
+
self.spline_coefs = {}
|
|
449
|
+
fdat = self._feffdat
|
|
450
|
+
self.spline_coefs['pha'] = UnivariateSpline(fdat.k, fdat.pha, s=0)
|
|
451
|
+
self.spline_coefs['amp'] = UnivariateSpline(fdat.k, fdat.amp, s=0)
|
|
452
|
+
self.spline_coefs['rep'] = UnivariateSpline(fdat.k, fdat.rep, s=0)
|
|
453
|
+
self.spline_coefs['lam'] = UnivariateSpline(fdat.k, fdat.lam, s=0)
|
|
454
|
+
|
|
455
|
+
def calc_chi(self, k=None, kmax=None, kstep=None, degen=None, s02=None,
|
|
456
|
+
e0=None, ei=None, deltar=None, sigma2=None,
|
|
457
|
+
third=None, fourth=None, interp='cubic', **kws):
|
|
458
|
+
"""calculate chi(k) with values provided for path parameters
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
"""
|
|
462
|
+
fdat = self._feffdat
|
|
463
|
+
|
|
464
|
+
if degen is None: degen = self.degen
|
|
465
|
+
if s02 is None: s02 = self.s02
|
|
466
|
+
if e0 is None: e0 = self.e0
|
|
467
|
+
if ei is None: ei = self.ei
|
|
468
|
+
if deltar is None: deltar = self.deltar
|
|
469
|
+
if sigma2 is None: sigma2 = self.sigma2
|
|
470
|
+
if third is None: third = self.third
|
|
471
|
+
if fourth is None: fourth = self.fourth
|
|
472
|
+
|
|
473
|
+
if fdat.reff < 0.05:
|
|
474
|
+
print('reff is too small to calculate chi(k)')
|
|
475
|
+
return
|
|
476
|
+
# make sure we have a k array
|
|
477
|
+
if k is None:
|
|
478
|
+
if kmax is None:
|
|
479
|
+
kmax = 30.0
|
|
480
|
+
kmax = min(max(fdat.k), kmax)
|
|
481
|
+
if kstep is None:
|
|
482
|
+
kstep = 0.05
|
|
483
|
+
k = kstep * np.arange(int(1.01 + kmax/kstep), dtype='float64')
|
|
484
|
+
|
|
485
|
+
reff = fdat.reff
|
|
486
|
+
# get values for all the path parameters
|
|
487
|
+
|
|
488
|
+
# create e0-shifted energy and k, careful to look for |e0| ~= 0.
|
|
489
|
+
en = k*k - e0*ETOK
|
|
490
|
+
if min(abs(en)) < SMALL_ENERGY:
|
|
491
|
+
try:
|
|
492
|
+
en[np.where(abs(en) < 1.5*SMALL_ENERGY)] = SMALL_ENERGY
|
|
493
|
+
except ValueError:
|
|
494
|
+
pass
|
|
495
|
+
# q is the e0-shifted wavenumber
|
|
496
|
+
q = np.sign(en)*np.sqrt(abs(en))
|
|
497
|
+
|
|
498
|
+
# lookup Feff.dat values (pha, amp, rep, lam)
|
|
499
|
+
if interp.startswith('lin'):
|
|
500
|
+
pha = np.interp(q, fdat.k, fdat.pha)
|
|
501
|
+
amp = np.interp(q, fdat.k, fdat.amp)
|
|
502
|
+
rep = np.interp(q, fdat.k, fdat.rep)
|
|
503
|
+
lam = np.interp(q, fdat.k, fdat.lam)
|
|
504
|
+
else:
|
|
505
|
+
pha = self.spline_coefs['pha'](q)
|
|
506
|
+
amp = self.spline_coefs['amp'](q)
|
|
507
|
+
rep = self.spline_coefs['rep'](q)
|
|
508
|
+
lam = self.spline_coefs['lam'](q)
|
|
509
|
+
|
|
510
|
+
# p = complex wavenumber, and its square:
|
|
511
|
+
pp = (rep + 1j/lam)**2 + 1j * ei * ETOK
|
|
512
|
+
p = np.sqrt(pp)
|
|
513
|
+
|
|
514
|
+
# the xafs equation:
|
|
515
|
+
cchi = np.exp(-2*reff*p.imag - 2*pp*(sigma2 - pp*fourth/3) +
|
|
516
|
+
1j*(2*q*reff + pha +
|
|
517
|
+
2*p*(deltar - 2*sigma2/reff - 2*pp*third/3) ))
|
|
518
|
+
|
|
519
|
+
cchi = self.degen * s02 * amp * cchi / (q*(reff + deltar)**2)
|
|
520
|
+
cchi[0] = 2*cchi[1] - cchi[2]
|
|
521
|
+
# outputs:
|
|
522
|
+
self.k = k
|
|
523
|
+
self.p = p
|
|
524
|
+
self.chi = cchi.imag
|
|
525
|
+
self.chi_imag = -cchi.real
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
def feffpath(filename='', label='', feffrun='', s02=None, degen=None,
|
|
530
|
+
e0=None,ei=None, deltar=None, sigma2=None, third=None,
|
|
531
|
+
fourth=None, **kws):
|
|
532
|
+
"""create a Feff Path Group from a *feffNNNN.dat* file.
|
|
533
|
+
|
|
534
|
+
Parameters:
|
|
535
|
+
-----------
|
|
536
|
+
filename: name (full path of) *feffNNNN.dat* file
|
|
537
|
+
label: label for path [file name]
|
|
538
|
+
degen: path degeneracy, N [taken from file]
|
|
539
|
+
s02: S_0^2 value or parameter [1.0]
|
|
540
|
+
e0: E_0 value or parameter [0.0]
|
|
541
|
+
deltar: delta_R value or parameter [0.0]
|
|
542
|
+
sigma2: sigma^2 value or parameter [0.0]
|
|
543
|
+
third: c_3 value or parameter [0.0]
|
|
544
|
+
fourth: c_4 value or parameter [0.0]
|
|
545
|
+
ei: E_i value or parameter [0.0]
|
|
546
|
+
|
|
547
|
+
For all the options described as **value or parameter** either a
|
|
548
|
+
numerical value or a Parameter (as created by param()) can be given.
|
|
549
|
+
|
|
550
|
+
Returns:
|
|
551
|
+
---------
|
|
552
|
+
a FeffPath object.
|
|
553
|
+
"""
|
|
554
|
+
if not pathname_valid(filename):
|
|
555
|
+
raise ValueError(f"Feff Path file '{filename:s}' not found")
|
|
556
|
+
|
|
557
|
+
return FeffPath(filename=filename, label=label, feffrun=feffrun,
|
|
558
|
+
s02=s02, degen=degen, e0=e0, ei=ei, deltar=deltar,
|
|
559
|
+
sigma2=sigma2, third=third, fourth=fourth)
|
feffdb/schema.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
VERSIONS = [(1, 'beta1', '2026-August-30', 'pre-release'),
|
|
2
|
+
]
|
|
3
|
+
|
|
4
|
+
feffdb_schema = """
|
|
5
|
+
|
|
6
|
+
PRAGMA journal_mode=WAL;
|
|
7
|
+
PRAGMA foreign_keys=ON;
|
|
8
|
+
PRAGMA page_size=8192;
|
|
9
|
+
|
|
10
|
+
create table version (id integer primary key,
|
|
11
|
+
tag text not null,
|
|
12
|
+
date text not null,
|
|
13
|
+
notes text);
|
|
14
|
+
|
|
15
|
+
create table info (key text primary key not null, value text not null);
|
|
16
|
+
|
|
17
|
+
create table element (z integer primary key not null, symbol text not null,
|
|
18
|
+
name text not null, mass float not null);
|
|
19
|
+
|
|
20
|
+
create table cif (id integer primary key autoincrement,
|
|
21
|
+
ciftext blob not null,
|
|
22
|
+
space_group text not null,
|
|
23
|
+
formula text,
|
|
24
|
+
compound text,
|
|
25
|
+
source_db text,
|
|
26
|
+
source_id text);
|
|
27
|
+
|
|
28
|
+
create table feffinp (id integer primary key autoincrement,
|
|
29
|
+
inptext blob not null,
|
|
30
|
+
absorber integer not null,
|
|
31
|
+
edge text not null,
|
|
32
|
+
scatterers text not null,
|
|
33
|
+
natoms integer not null,
|
|
34
|
+
cif_id integer,
|
|
35
|
+
foreign key(cif_id) references cif(id),
|
|
36
|
+
foreign key(absorber) references element(z) );
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
create table feffdat (id integer primary key autoincrement,
|
|
40
|
+
absorber integer not null,
|
|
41
|
+
scatterer integer not null,
|
|
42
|
+
nleg integer not null,
|
|
43
|
+
reff float not null,
|
|
44
|
+
edge text not null,
|
|
45
|
+
degen integer not null,
|
|
46
|
+
geometry text not null,
|
|
47
|
+
label text,
|
|
48
|
+
description text,
|
|
49
|
+
feffinp_id integer,
|
|
50
|
+
feffdat blob not null,
|
|
51
|
+
donator_id integer default 1,
|
|
52
|
+
foreign key(feffinp_id) references feffinp(id),
|
|
53
|
+
foreign key(absorber) references element(z),
|
|
54
|
+
foreign key(scatterer) references element(z),
|
|
55
|
+
foreign key(donator_id) references person(id) );
|
|
56
|
+
|
|
57
|
+
create table person (id integer primary key autoincrement,
|
|
58
|
+
email text not null unique,
|
|
59
|
+
name text, password text);
|
|
60
|
+
|
|
61
|
+
insert into person (email, name) values ('feffdb@local', 'feffdb library');
|
|
62
|
+
|
|
63
|
+
create table rating_enum (scoreval integer primary key not null, notes text);
|
|
64
|
+
|
|
65
|
+
insert into rating_enum (scoreval) values (1);
|
|
66
|
+
insert into rating_enum (scoreval) values (2);
|
|
67
|
+
insert into rating_enum (scoreval) values (3);
|
|
68
|
+
insert into rating_enum (scoreval) values (4);
|
|
69
|
+
insert into rating_enum (scoreval) values (5);
|
|
70
|
+
|
|
71
|
+
create table feffdat_rating (id integer primary key autoincrement,
|
|
72
|
+
score integer not null,
|
|
73
|
+
review text,
|
|
74
|
+
feffdat_id integer not null,
|
|
75
|
+
person_id integer not null,
|
|
76
|
+
foreign key(score) references rating_enum(scoreval),
|
|
77
|
+
foreign key(feffdat_id) references feffdat(id),
|
|
78
|
+
foreign key(person_id) references person(id) );
|
|
79
|
+
|
|
80
|
+
"""
|