PyXplore 2025.7.17__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.
- PyXplore/Background/BacDeduct.py +474 -0
- PyXplore/Background/__init__.py +1 -0
- PyXplore/EMBraggOpt/BraggLawDerivation.py +651 -0
- PyXplore/EMBraggOpt/EMBraggSolver.py +1199 -0
- PyXplore/EMBraggOpt/WPEMFuns/SolverFuns.py +356 -0
- PyXplore/EMBraggOpt/WPEMFuns/__init__.py +1 -0
- PyXplore/EMBraggOpt/__init__.py +1 -0
- PyXplore/Extinction/CifReader.py +238 -0
- PyXplore/Extinction/Relaxer.py +71 -0
- PyXplore/Extinction/XRDpre.py +892 -0
- PyXplore/Extinction/__init__.py +1 -0
- PyXplore/Extinction/m3gnet/__init__.py +4 -0
- PyXplore/Extinction/m3gnet/callbacks.py +26 -0
- PyXplore/Extinction/m3gnet/cli.py +112 -0
- PyXplore/Extinction/m3gnet/config.py +53 -0
- PyXplore/Extinction/m3gnet/graph/__init__.py +31 -0
- PyXplore/Extinction/m3gnet/graph/_batch.py +270 -0
- PyXplore/Extinction/m3gnet/graph/_compute.py +184 -0
- PyXplore/Extinction/m3gnet/graph/_converters.py +224 -0
- PyXplore/Extinction/m3gnet/graph/_structure.py +72 -0
- PyXplore/Extinction/m3gnet/graph/_types.py +387 -0
- PyXplore/Extinction/m3gnet/layers/__init__.py +57 -0
- PyXplore/Extinction/m3gnet/layers/_aggregate.py +60 -0
- PyXplore/Extinction/m3gnet/layers/_atom.py +107 -0
- PyXplore/Extinction/m3gnet/layers/_atom_ref.py +168 -0
- PyXplore/Extinction/m3gnet/layers/_base.py +87 -0
- PyXplore/Extinction/m3gnet/layers/_basis.py +63 -0
- PyXplore/Extinction/m3gnet/layers/_bond.py +232 -0
- PyXplore/Extinction/m3gnet/layers/_core.py +246 -0
- PyXplore/Extinction/m3gnet/layers/_cutoff.py +36 -0
- PyXplore/Extinction/m3gnet/layers/_gn.py +255 -0
- PyXplore/Extinction/m3gnet/layers/_readout.py +266 -0
- PyXplore/Extinction/m3gnet/layers/_state.py +101 -0
- PyXplore/Extinction/m3gnet/layers/_three_body.py +73 -0
- PyXplore/Extinction/m3gnet/layers/_two_body.py +48 -0
- PyXplore/Extinction/m3gnet/models/__init__.py +16 -0
- PyXplore/Extinction/m3gnet/models/_base.py +261 -0
- PyXplore/Extinction/m3gnet/models/_dynamics.py +384 -0
- PyXplore/Extinction/m3gnet/models/_m3gnet.py +369 -0
- PyXplore/Extinction/m3gnet/trainers/__init__.py +5 -0
- PyXplore/Extinction/m3gnet/trainers/_metrics.py +33 -0
- PyXplore/Extinction/m3gnet/trainers/_potential.py +266 -0
- PyXplore/Extinction/m3gnet/trainers/_property.py +288 -0
- PyXplore/Extinction/m3gnet/type.py +11 -0
- PyXplore/Extinction/m3gnet/utils/__init__.py +49 -0
- PyXplore/Extinction/m3gnet/utils/_general.py +73 -0
- PyXplore/Extinction/m3gnet/utils/_math.py +386 -0
- PyXplore/Extinction/m3gnet/utils/_tf.py +157 -0
- PyXplore/Extinction/wyckoff/__init__.py +1 -0
- PyXplore/Extinction/wyckoff/wyckoff_dict.py +237 -0
- PyXplore/GraphStructure/__init__.py +1 -0
- PyXplore/GraphStructure/graph.py +358 -0
- PyXplore/Plot/UnitCell.py +125 -0
- PyXplore/Plot/__init__.py +1 -0
- PyXplore/Refinement/VolumeFractionDertermination.py +223 -0
- PyXplore/Refinement/__init__.py +1 -0
- PyXplore/StructureOpt/SiteOpt.py +378 -0
- PyXplore/StructureOpt/__init__.py +1 -0
- PyXplore/WPEM.py +678 -0
- PyXplore/WPEMXAS/EXAFS.py +334 -0
- PyXplore/WPEMXAS/__init__.py +1 -0
- PyXplore/WPEMXAS/fftdemo.ipynb +207 -0
- PyXplore/WPEMXPS/XPSEM.py +1010 -0
- PyXplore/WPEMXPS/__init__.py +1 -0
- PyXplore/XRDSimulation/DiffractionGrometry/__init__.py +1 -0
- PyXplore/XRDSimulation/DiffractionGrometry/atom.py +6 -0
- PyXplore/XRDSimulation/Simulation.py +628 -0
- PyXplore/XRDSimulation/__init__.py +1 -0
- PyXplore/__init__.py +11 -0
- PyXplore/refs/International_Union_of_Crystallography.pdf +0 -0
- PyXplore/refs/WPEM_Manual.pdf +0 -0
- PyXplore-2025.7.17.dist-info/METADATA +60 -0
- PyXplore-2025.7.17.dist-info/RECORD +75 -0
- PyXplore-2025.7.17.dist-info/WHEEL +5 -0
- PyXplore-2025.7.17.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
# The background intensity distribution module of WPEM
|
|
2
|
+
# Author: Bin CAO <binjacobcao@gmail.com>
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
import numpy as np
|
|
6
|
+
import copy
|
|
7
|
+
import os
|
|
8
|
+
import heapq
|
|
9
|
+
import numpy.fft as nf
|
|
10
|
+
from scipy.signal import savgol_filter
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
from sklearn.gaussian_process import GaussianProcessRegressor as Gpr
|
|
13
|
+
from sklearn.gaussian_process.kernels import RBF, WhiteKernel
|
|
14
|
+
import xml.etree.ElementTree as ET
|
|
15
|
+
import re
|
|
16
|
+
import pandas as pd
|
|
17
|
+
|
|
18
|
+
class TwiceFilter:
|
|
19
|
+
"""
|
|
20
|
+
background intensity module
|
|
21
|
+
Smoothing technique Savitzky-Golay filter is applied to improve the signal/noise ratio
|
|
22
|
+
after inverse Fourier transform and give a set of slowly varying background points
|
|
23
|
+
"""
|
|
24
|
+
def __init__(self, Model='XRD', segement=None, work_dir = None):
|
|
25
|
+
"""
|
|
26
|
+
Display the background curve of XRD diffraction spectrum (Model='XRD')
|
|
27
|
+
and Raman spectrum (Model='Raman') according to the type
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
# Model = 'XRD' or 'Raman' or 'XPS
|
|
31
|
+
self.Model = Model
|
|
32
|
+
self.segement = segement
|
|
33
|
+
if work_dir is None:
|
|
34
|
+
self.dir = 'ConvertedDocuments'
|
|
35
|
+
else:
|
|
36
|
+
self.dir = os.path.join(work_dir, 'ConvertedDocuments')
|
|
37
|
+
|
|
38
|
+
os.makedirs(self.dir, exist_ok=True)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def FFTandSGFilter(self, intensity_csv, LFctg = 0.5, lowAngleRange=None, bac_num=None, bac_split=5, window_length=17,
|
|
42
|
+
polyorder=3, poly_n=6, mode='nearest', bac_var_type='constant',noise=None):
|
|
43
|
+
"""
|
|
44
|
+
:param intensity_csv: the experimental observation
|
|
45
|
+
|
|
46
|
+
:param LFctg: low frequency filter Percentage, default = 0.5
|
|
47
|
+
|
|
48
|
+
:param lowAngleRange: low angle (2theta) with obvious background lift phenomenon
|
|
49
|
+
|
|
50
|
+
:param bac_num: the number of background points in the background set
|
|
51
|
+
|
|
52
|
+
:param bac_split: the background spectrum is divided into several segments
|
|
53
|
+
|
|
54
|
+
:param window_length : int
|
|
55
|
+
The length of the filter window (i.e., the number of coefficients).
|
|
56
|
+
`window_length` must be a positive odd integer. If `mode` is 'interp',
|
|
57
|
+
`window_length` must be less than or equal to the size of `x`.
|
|
58
|
+
|
|
59
|
+
:param polyorder: int
|
|
60
|
+
The order of the polynomial used to fit the samples.
|
|
61
|
+
`polyorder` must be less than `window_length`.
|
|
62
|
+
|
|
63
|
+
:param poly_n: background mean function fitting polynomial degree
|
|
64
|
+
|
|
65
|
+
:param mode: str, optional
|
|
66
|
+
Must be 'mirror', 'constant', 'nearest', 'wrap' or 'interp'. This
|
|
67
|
+
determines the type of extension to use for the padded signal to
|
|
68
|
+
which the filter is applied. When `mode` is 'constant', the padding
|
|
69
|
+
value is given by `cval`. See the Notes for more details on 'mirror',
|
|
70
|
+
'constant', 'wrap', and 'nearest'.
|
|
71
|
+
When the 'interp' mode is selected (the default), no extension
|
|
72
|
+
is used. Instead, a degree `polyorder` polynomial is fit to the
|
|
73
|
+
last `window_length` values of the edges, and this polynomial is
|
|
74
|
+
used to evaluate the last `window_length // 2` output values.
|
|
75
|
+
|
|
76
|
+
:param bac_var_type:
|
|
77
|
+
A pattern describing the background distribution
|
|
78
|
+
one of constant, polynomial, multivariate gaussian
|
|
79
|
+
|
|
80
|
+
:param noise:
|
|
81
|
+
float, default is None
|
|
82
|
+
the noise level applied to gaussian processes model
|
|
83
|
+
|
|
84
|
+
:return:
|
|
85
|
+
"""
|
|
86
|
+
# Define the font of the image
|
|
87
|
+
plt.rcParams['font.family'] = 'sans-serif'
|
|
88
|
+
plt.rcParams['font.size'] = 12
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
angle = intensity_csv.iloc[:, 0]
|
|
92
|
+
signal = intensity_csv.iloc[:, 1]
|
|
93
|
+
if self.Model == 'XPS':
|
|
94
|
+
# in usual, the experimental XPS energy is recorded in a descreasing order
|
|
95
|
+
angle, signal = reorder_vector(angle, signal )
|
|
96
|
+
# The complex-valued FFT computes the Fourier transform of signal,
|
|
97
|
+
# which represents the decomposition of the signal into its component frequencies.
|
|
98
|
+
complex_array = nf.fft(signal)
|
|
99
|
+
pows = np.abs(complex_array)
|
|
100
|
+
|
|
101
|
+
DC = []
|
|
102
|
+
# tuple_type = np.where(complex_array.imag == 0)[0]
|
|
103
|
+
# for i in tuple_type:
|
|
104
|
+
# DC.append(tuple_type[i])
|
|
105
|
+
hDCnum = int(LFctg * len(angle))
|
|
106
|
+
for hDC in range(hDCnum):
|
|
107
|
+
DC.append(heapq.nsmallest(hDCnum, enumerate(pows), key=lambda x: x[1])[hDC][0])
|
|
108
|
+
|
|
109
|
+
filter_complex_array = copy.deepcopy(complex_array)
|
|
110
|
+
filter_complex_array[DC] = 0
|
|
111
|
+
FFT_filter_intensity = nf.ifft(filter_complex_array).real
|
|
112
|
+
|
|
113
|
+
# Savitzky-Golay Filter
|
|
114
|
+
intensity_up = copy.deepcopy(FFT_filter_intensity)
|
|
115
|
+
if bac_num == None:
|
|
116
|
+
bac_num = int(1 / 4 * len(angle))
|
|
117
|
+
else:
|
|
118
|
+
bac_num = bac_num
|
|
119
|
+
SG_filter_intensity = savgol_filter(intensity_up, window_length, polyorder, mode=mode)
|
|
120
|
+
|
|
121
|
+
PairIndex = []
|
|
122
|
+
Twotheta = []
|
|
123
|
+
BacSelected = []
|
|
124
|
+
BacOrigPoint = []
|
|
125
|
+
if bac_split == None:
|
|
126
|
+
print('You must input the parameter bac_split')
|
|
127
|
+
|
|
128
|
+
if self.segement == None:
|
|
129
|
+
if type(bac_split) == int:
|
|
130
|
+
if lowAngleRange == None:
|
|
131
|
+
choiselg_num = int(bac_num / bac_split)
|
|
132
|
+
Split_filter_intensity = self.chunks(SG_filter_intensity, bac_split)
|
|
133
|
+
for part in range(bac_split):
|
|
134
|
+
for num in range(choiselg_num):
|
|
135
|
+
PairIndex.append((heapq.nsmallest(choiselg_num, enumerate(Split_filter_intensity[part]),
|
|
136
|
+
key=lambda x: x[1])[num][0]) + part * len(Split_filter_intensity[0]))
|
|
137
|
+
|
|
138
|
+
elif type(lowAngleRange) == int or type(lowAngleRange) == float:
|
|
139
|
+
lowAngleRange_index = np.where(angle >= lowAngleRange)[0][0]
|
|
140
|
+
lowangle_num = np.max((int(bac_num * (lowAngleRange_index / len(angle))), int(0.2*(lowAngleRange_index+1))))
|
|
141
|
+
choiselg_num = np.max((int(lowangle_num / 5), 1))
|
|
142
|
+
other_num = bac_num - lowangle_num
|
|
143
|
+
Split_filter_intensity = self.chunks(SG_filter_intensity[0:lowAngleRange_index], 5)
|
|
144
|
+
for part in range(len(Split_filter_intensity)):
|
|
145
|
+
for num in range(choiselg_num):
|
|
146
|
+
PairIndex.append((heapq.nsmallest(choiselg_num, enumerate(Split_filter_intensity[part]),
|
|
147
|
+
key=lambda x: x[1])[num][0]) + part * len(Split_filter_intensity[0]))
|
|
148
|
+
|
|
149
|
+
__choiselg_num = int(other_num / bac_split)
|
|
150
|
+
__Split_filter_intensity = self.chunks(SG_filter_intensity[lowAngleRange_index:-1], bac_split)
|
|
151
|
+
|
|
152
|
+
for __part in range(bac_split):
|
|
153
|
+
for __num in range(__choiselg_num):
|
|
154
|
+
PairIndex.append((heapq.nsmallest(__choiselg_num, enumerate(__Split_filter_intensity[__part]),
|
|
155
|
+
key=lambda x: x[1])[__num][0]) + __part * len(__Split_filter_intensity[0]) + lowAngleRange_index)
|
|
156
|
+
else:
|
|
157
|
+
print('Type Error \'lowAngleRange\'')
|
|
158
|
+
|
|
159
|
+
else:
|
|
160
|
+
print('Type Error \'segmentation strategy\'')
|
|
161
|
+
|
|
162
|
+
elif type(self.segement) == list:
|
|
163
|
+
total_length = 0
|
|
164
|
+
for seg in range(len(self.segement)):
|
|
165
|
+
total_length += (self.segement[seg][1] - self.segement[seg][0])
|
|
166
|
+
|
|
167
|
+
choiselg_num = []
|
|
168
|
+
for seg in range(len(self.segement)):
|
|
169
|
+
choiselg_num.append(int(bac_num * (self.segement[seg][1] - self.segement[seg][0]) / total_length))
|
|
170
|
+
for seg in range(len(self.segement)):
|
|
171
|
+
low_index = np.argmin(np.abs(angle - self.segement[seg][0]))
|
|
172
|
+
up_index = np.argmin(np.abs(angle - self.segement[seg][1]))
|
|
173
|
+
int_segement = SG_filter_intensity[low_index:up_index]
|
|
174
|
+
num = choiselg_num[seg]
|
|
175
|
+
|
|
176
|
+
points = heapq.nsmallest(num, enumerate(int_segement), key=lambda x: x[1])
|
|
177
|
+
for k in range(len(points)):
|
|
178
|
+
PairIndex.append(points[k][0] + low_index)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
PairIndex.sort()
|
|
182
|
+
for pair in range(len(PairIndex)):
|
|
183
|
+
Twotheta.append(angle[PairIndex[pair]])
|
|
184
|
+
# intensity of experimental measurement
|
|
185
|
+
BacOrigPoint.append(signal[PairIndex[pair]])
|
|
186
|
+
# intensity after fft and SG filter
|
|
187
|
+
BacSelected.append(SG_filter_intensity[PairIndex[pair]])
|
|
188
|
+
|
|
189
|
+
# calculate the variance of background function
|
|
190
|
+
if bac_var_type == 'constant' :
|
|
191
|
+
Sum = 0
|
|
192
|
+
for bac in range(len(BacSelected)):
|
|
193
|
+
Sum += (BacOrigPoint[bac] - BacSelected[bac]) ** 2
|
|
194
|
+
Var_2 = Sum / len(BacOrigPoint) # 1/n * sum( (x-E(x))^2 )
|
|
195
|
+
standard_deviation = float(np.sqrt(Var_2))
|
|
196
|
+
|
|
197
|
+
bac = np.polyfit(Twotheta, BacSelected, poly_n)
|
|
198
|
+
poly_fit = np.poly1d(bac)
|
|
199
|
+
Intensity_mean = poly_fit(angle)
|
|
200
|
+
inten_no_bac = list(map(lambda x: abs(x[0] - x[1]), zip(signal, Intensity_mean)))
|
|
201
|
+
# Save non-background intensity
|
|
202
|
+
with open(os.path.join(self.dir, "no_bac_intensity.csv"), 'w') as wfid:
|
|
203
|
+
for j in range(len(angle)):
|
|
204
|
+
print(angle[j], end=', ', file=wfid)
|
|
205
|
+
print(float(inten_no_bac[j]), file=wfid)
|
|
206
|
+
# Save background intensity
|
|
207
|
+
with open(os.path.join(self.dir, "bac.csv"), 'w') as wfid:
|
|
208
|
+
for j in range(len(angle)):
|
|
209
|
+
print(angle[j], end=', ', file=wfid)
|
|
210
|
+
print(float(Intensity_mean[j]), file=wfid)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
plt.plot(angle, Intensity_mean, color='k', label='background means')
|
|
214
|
+
lowbound = Intensity_mean - standard_deviation
|
|
215
|
+
upbound = Intensity_mean + standard_deviation
|
|
216
|
+
|
|
217
|
+
plt.plot(angle, lowbound, 'g--', lw=1)
|
|
218
|
+
plt.plot(angle, upbound, 'g--', lw=1)
|
|
219
|
+
plt.fill_between(angle, lowbound, upbound,
|
|
220
|
+
color='lightblue', label='one sigma \n confidence interval')
|
|
221
|
+
plt.scatter(Twotheta, BacSelected, color='r', s=5,zorder=2, label='background points')
|
|
222
|
+
if self.Model == 'Raman':
|
|
223
|
+
plt.xlabel('Raman shift (cm\u207B\u00B9)')
|
|
224
|
+
elif self.Model == 'XPS':
|
|
225
|
+
plt.xlabel('Binding energy (eV)')
|
|
226
|
+
else:
|
|
227
|
+
plt.xlabel('2\u03b8\u00B0')
|
|
228
|
+
plt.ylabel('I (a.u.)')
|
|
229
|
+
plt.legend()
|
|
230
|
+
plt.savefig(os.path.join(self.dir, 'background_function_distribution_constant.png'), dpi=800)
|
|
231
|
+
plt.show()
|
|
232
|
+
plt.clf()
|
|
233
|
+
|
|
234
|
+
# Be careful! if choose 'polynomial', WPEM models the variance as a three-times polynomial function of diffraction angles
|
|
235
|
+
# the corresponding background function is fitted by a polynomial function latter
|
|
236
|
+
elif bac_var_type == 'polynomial' :
|
|
237
|
+
sta_dev_array = list(map(lambda x: abs(x[0]-x[1]), zip(BacOrigPoint,BacSelected)))
|
|
238
|
+
bac_var = np.polyfit(Twotheta, sta_dev_array, 3) # here default ploy = 3
|
|
239
|
+
poly_var_fit = np.poly1d(bac_var)
|
|
240
|
+
standard_deviation = poly_var_fit(angle) # here standard_deviation is a N (2theta) array
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
bac = np.polyfit(Twotheta, BacSelected, poly_n)
|
|
244
|
+
poly_fit = np.poly1d(bac)
|
|
245
|
+
Intensity_mean = poly_fit(angle)
|
|
246
|
+
inten_no_bac = list(map(lambda x: abs(x[0] - x[1]), zip(signal, Intensity_mean)))
|
|
247
|
+
# Save non-background intensity
|
|
248
|
+
with open(os.path.join(self.dir, "no_bac_intensity.csv"), 'w') as wfid:
|
|
249
|
+
for j in range(len(angle)):
|
|
250
|
+
print(angle[j], end=', ', file=wfid)
|
|
251
|
+
print(float(inten_no_bac[j]), file=wfid)
|
|
252
|
+
# Save background intensity
|
|
253
|
+
with open(os.path.join(self.dir, "bac.csv"), 'w') as wfid:
|
|
254
|
+
for j in range(len(angle)):
|
|
255
|
+
print(angle[j], end=', ', file=wfid)
|
|
256
|
+
print(float(Intensity_mean[j]), file=wfid)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
plt.plot(angle, Intensity_mean, color='k', label='background means')
|
|
260
|
+
lowbound = list(map(lambda x: abs(x[0] - x[1]), zip(Intensity_mean, standard_deviation)))
|
|
261
|
+
upbound = list(map(lambda x: abs(x[0] + x[1]), zip(Intensity_mean, standard_deviation)))
|
|
262
|
+
|
|
263
|
+
plt.plot(angle, lowbound, 'g--', lw=1)
|
|
264
|
+
plt.plot(angle, upbound, 'g--', lw=1)
|
|
265
|
+
plt.fill_between(angle, lowbound, upbound,
|
|
266
|
+
color='lightblue', label='one sigma \n confidence interval')
|
|
267
|
+
plt.scatter(Twotheta, BacSelected, color='r', s=5, zorder=2,label='background points')
|
|
268
|
+
if self.Model == 'Raman':
|
|
269
|
+
plt.xlabel('Raman shift (cm\u207B\u00B9)')
|
|
270
|
+
elif self.Model == 'XPS':
|
|
271
|
+
plt.xlabel('Binding energy (eV)')
|
|
272
|
+
else:
|
|
273
|
+
plt.xlabel('2\u03b8\u00B0')
|
|
274
|
+
plt.ylabel('I (a.u.)')
|
|
275
|
+
plt.legend()
|
|
276
|
+
plt.savefig(os.path.join(self.dir, 'background function distribution_polynomial.png'), dpi=800)
|
|
277
|
+
plt.show()
|
|
278
|
+
plt.clf()
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
# if choose 'multivariate gaussian', WPEM models the background function as a multivariate gaussian of diffraction angles with variance
|
|
282
|
+
elif bac_var_type == 'multivariate gaussian' :
|
|
283
|
+
print('hetet')
|
|
284
|
+
if noise == None :
|
|
285
|
+
kernel = 1 * RBF() + WhiteKernel()
|
|
286
|
+
model = Gpr(kernel = kernel, n_restarts_optimizer = 10, alpha = 0, normalize_y = True, random_state = 0).fit(np.array(Twotheta).reshape(-1,1), BacOrigPoint)
|
|
287
|
+
elif type(noise) == float:
|
|
288
|
+
kernel = 1 * RBF()
|
|
289
|
+
model = Gpr(kernel = kernel, n_restarts_optimizer = 10, alpha = noise, normalize_y = True, random_state = 0).fit(np.array(Twotheta).reshape(-1,1), BacOrigPoint)
|
|
290
|
+
else:
|
|
291
|
+
print('Warning - the type of noise must be float or None')
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
background_meanfunction, standard_deviation = model.predict(np.array(angle).reshape(-1,1), return_std=True)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
Intensity_mean, Intensity_dev = model.predict(angle[:, np.newaxis], return_std=True)
|
|
298
|
+
|
|
299
|
+
inten_no_bac = list(map(lambda x: abs(x[0] - x[1]), zip(signal, background_meanfunction)))
|
|
300
|
+
|
|
301
|
+
# Save non-background intensity
|
|
302
|
+
with open(os.path.join(self.dir, "no_bac_intensity.csv"), 'w') as wfid:
|
|
303
|
+
for j in range(len(angle)):
|
|
304
|
+
print(angle[j], end=', ', file=wfid)
|
|
305
|
+
print(float(inten_no_bac[j]), file=wfid)
|
|
306
|
+
# Save background intensity
|
|
307
|
+
with open(os.path.join(self.dir, "bac.csv"), 'w') as wfid:
|
|
308
|
+
for j in range(len(angle)):
|
|
309
|
+
print(angle[j], end=', ', file=wfid)
|
|
310
|
+
print(float(background_meanfunction[j]), file=wfid)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
plt.plot(angle, background_meanfunction, color='k', label='background means')
|
|
314
|
+
plt.plot(angle, Intensity_mean - Intensity_dev, 'g--',lw=1)
|
|
315
|
+
plt.plot(angle, Intensity_mean + Intensity_dev, 'g--',lw=1)
|
|
316
|
+
plt.fill_between(angle, Intensity_mean - Intensity_dev, Intensity_mean + Intensity_dev, color='lightblue', label='one sigma \n confidence interval')
|
|
317
|
+
plt.scatter(Twotheta, BacSelected, color='r', s=5, zorder=2,label='background points')
|
|
318
|
+
if self.Model == 'Raman':
|
|
319
|
+
plt.xlabel('Raman shift/(cm-1)')
|
|
320
|
+
elif self.Model == 'XPS':
|
|
321
|
+
plt.xlabel('Binding energy (eV)')
|
|
322
|
+
else:
|
|
323
|
+
plt.xlabel('2\u03b8\u00B0')
|
|
324
|
+
plt.ylabel('I (a.u.)')
|
|
325
|
+
plt.legend()
|
|
326
|
+
plt.savefig(os.path.join(self.dir, 'background function distribution _ multivariate gaussian.png'), dpi=800)
|
|
327
|
+
plt.show()
|
|
328
|
+
plt.clf()
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
# Save intensity document after FFT with DC
|
|
333
|
+
with open(os.path.join(self.dir, "intensity_fft.csv"), 'w') as wfid:
|
|
334
|
+
for j in range(len(angle)):
|
|
335
|
+
print(angle[j], end=', ', file=wfid)
|
|
336
|
+
print(float(FFT_filter_intensity[j]), file=wfid)
|
|
337
|
+
|
|
338
|
+
# Save background points intensity after FFT and SG filter
|
|
339
|
+
with open(os.path.join(self.dir, "bac_points.csv"), 'w') as wfid:
|
|
340
|
+
for j in range(len(Twotheta)):
|
|
341
|
+
print(Twotheta[j], end=', ', file=wfid)
|
|
342
|
+
print(float(BacSelected[j]), file=wfid)
|
|
343
|
+
|
|
344
|
+
plt.plot(angle, signal, color='cyan', label='original intensity')
|
|
345
|
+
plt.plot(angle, SG_filter_intensity, color='k', label='FFT-SG intensity')
|
|
346
|
+
plt.scatter(Twotheta, BacSelected, s=5, c='r', zorder=2,label='selected background points')
|
|
347
|
+
if self.Model == 'Raman':
|
|
348
|
+
plt.xlabel('Raman shift (cm\u207B\u00B9)')
|
|
349
|
+
elif self.Model == 'XPS':
|
|
350
|
+
plt.xlabel('Binding energy (eV)')
|
|
351
|
+
else:
|
|
352
|
+
plt.xlabel('2\u03b8\u00B0')
|
|
353
|
+
plt.ylabel('I (a.u.)')
|
|
354
|
+
plt.legend()
|
|
355
|
+
plt.savefig(os.path.join(self.dir, 'background points.png'),dpi=800)
|
|
356
|
+
plt.show()
|
|
357
|
+
plt.clf()
|
|
358
|
+
|
|
359
|
+
plt.plot(angle, signal, color='cyan', label='original intensity')
|
|
360
|
+
plt.plot(angle, Intensity_mean, color='k', label='background function')
|
|
361
|
+
plt.scatter(Twotheta, BacSelected, c='r',zorder=2, label='background points')
|
|
362
|
+
if self.Model == 'Raman':
|
|
363
|
+
plt.xlabel('Raman shift (cm\u207B\u00B9)')
|
|
364
|
+
elif self.Model == 'XPS':
|
|
365
|
+
plt.xlabel('Binding energy (eV)')
|
|
366
|
+
else:
|
|
367
|
+
plt.xlabel('2\u03b8\u00B0')
|
|
368
|
+
plt.ylabel('I (a.u.)')
|
|
369
|
+
plt.legend()
|
|
370
|
+
plt.savefig(os.path.join(self.dir, 'backgroundfittingcurve.png'),dpi=800)
|
|
371
|
+
plt.show()
|
|
372
|
+
plt.clf()
|
|
373
|
+
|
|
374
|
+
plt.plot(angle, signal, color='cyan', label='original intensity')
|
|
375
|
+
plt.plot(angle, inten_no_bac, color='k', label='de_background intensity')
|
|
376
|
+
if self.Model == 'Raman':
|
|
377
|
+
plt.xlabel('Raman shift (cm\u207B\u00B9)')
|
|
378
|
+
elif self.Model == 'XPS':
|
|
379
|
+
plt.xlabel('Binding energy (eV)')
|
|
380
|
+
else:
|
|
381
|
+
plt.xlabel('2\u03b8\u00B0')
|
|
382
|
+
plt.ylabel('I (a.u.)')
|
|
383
|
+
plt.legend()
|
|
384
|
+
plt.savefig(os.path.join(self.dir, 'de_backgroundfittingcurve.png'),dpi=800)
|
|
385
|
+
plt.show()
|
|
386
|
+
plt.clf()
|
|
387
|
+
print('\n================================')
|
|
388
|
+
return standard_deviation
|
|
389
|
+
|
|
390
|
+
def chunks(self, arr, m):
|
|
391
|
+
"""
|
|
392
|
+
Auxiliary function for splitting the array into m segments
|
|
393
|
+
"""
|
|
394
|
+
import math
|
|
395
|
+
arr = arr
|
|
396
|
+
m = m
|
|
397
|
+
n = int(math.ceil(len(arr) / float(m)))
|
|
398
|
+
return [arr[i:i + n] for i in range(0, len(arr), n)]
|
|
399
|
+
|
|
400
|
+
def reorder_vector(EBenergy, inten):
|
|
401
|
+
# Create a list of tuples containing the EBenergy and inten values
|
|
402
|
+
data = list(zip(EBenergy, inten))
|
|
403
|
+
# Sort the data based on the EBenergy values in ascending order
|
|
404
|
+
sorted_data = sorted(data, key=lambda x: x[0])
|
|
405
|
+
# Extract the sorted EBenergy and inten values into separate lists
|
|
406
|
+
sorted_EBenergy, sorted_inten = zip(*sorted_data)
|
|
407
|
+
return np.array(sorted_EBenergy), np.array(sorted_inten)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def convert_file(file_name):
|
|
411
|
+
"""
|
|
412
|
+
Convert "Free Format(2Theta, step, 2ThetaF)" to "X,Y Data"
|
|
413
|
+
|
|
414
|
+
This function is defined to convert the XRD diffractometer output file format
|
|
415
|
+
into a WPEM acceptable input file
|
|
416
|
+
|
|
417
|
+
file_name : The file name of original XRD data (Free Format(2Theta, step, 2ThetaF))
|
|
418
|
+
"""
|
|
419
|
+
digital_pattern = re.compile(r'[0-9.]+')
|
|
420
|
+
intensity = []
|
|
421
|
+
with open(file_name, 'r') as xrdfid:
|
|
422
|
+
first_line = xrdfid.readline()
|
|
423
|
+
tmp_list = digital_pattern.findall(first_line)
|
|
424
|
+
start = float(tmp_list[0])
|
|
425
|
+
step = float(tmp_list[1])
|
|
426
|
+
end = float(tmp_list[2])
|
|
427
|
+
while tmp_list:
|
|
428
|
+
tmp_list = digital_pattern.findall(xrdfid.readline())
|
|
429
|
+
for i in tmp_list:
|
|
430
|
+
intensity.append(float(i))
|
|
431
|
+
|
|
432
|
+
two_theta = np.arange(start, end + step, step)
|
|
433
|
+
with open(os.path.join(self.dir, 'intensity.csv'), 'w') as wfid:
|
|
434
|
+
for i in range(len(intensity)):
|
|
435
|
+
print(two_theta[i], end=',', file=wfid)
|
|
436
|
+
print(intensity[i], file=wfid)
|
|
437
|
+
return True
|
|
438
|
+
|
|
439
|
+
def read_xrdml(file):
|
|
440
|
+
"""
|
|
441
|
+
Takes a file path to an xrdml-file as argument and returns a pd with
|
|
442
|
+
2-theta as keys and intensity counts as values
|
|
443
|
+
"""
|
|
444
|
+
|
|
445
|
+
f = ET.parse(file)
|
|
446
|
+
root = f.getroot()
|
|
447
|
+
|
|
448
|
+
# Check for namespace
|
|
449
|
+
namespace_check = re.match(r'\{.*\}', root.tag)
|
|
450
|
+
namespace = '' if namespace_check is None else namespace_check.group(0)
|
|
451
|
+
|
|
452
|
+
# Get the scan section
|
|
453
|
+
scan = root.find(f'.//{namespace}scan')
|
|
454
|
+
# Extract the data
|
|
455
|
+
try:
|
|
456
|
+
intensities = scan.find(f'.//{namespace}intensities').text.split()
|
|
457
|
+
except:
|
|
458
|
+
intensities = scan.find(f'.//{namespace}counts').text.split()
|
|
459
|
+
intensities = [eval(x) for x in intensities]
|
|
460
|
+
|
|
461
|
+
# 2Theta data
|
|
462
|
+
axis = scan.find(f'.//{namespace}positions[@axis="2Theta"]')
|
|
463
|
+
|
|
464
|
+
startPosition = float(axis.find(f'.//{namespace}startPosition').text)
|
|
465
|
+
endPosition = float(axis.find(f'.//{namespace}endPosition').text)
|
|
466
|
+
step = (endPosition - startPosition) / (len(intensities) - 1)
|
|
467
|
+
|
|
468
|
+
tt = [startPosition + n*step for n in range(len(intensities))]
|
|
469
|
+
|
|
470
|
+
data = list(zip(tt, intensities))
|
|
471
|
+
df = pd.DataFrame(data)
|
|
472
|
+
df.to_csv(os.path.join(self.dir, 'intensity.csv'), index=False)
|
|
473
|
+
|
|
474
|
+
return True
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|