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.
Files changed (75) hide show
  1. PyXplore/Background/BacDeduct.py +474 -0
  2. PyXplore/Background/__init__.py +1 -0
  3. PyXplore/EMBraggOpt/BraggLawDerivation.py +651 -0
  4. PyXplore/EMBraggOpt/EMBraggSolver.py +1199 -0
  5. PyXplore/EMBraggOpt/WPEMFuns/SolverFuns.py +356 -0
  6. PyXplore/EMBraggOpt/WPEMFuns/__init__.py +1 -0
  7. PyXplore/EMBraggOpt/__init__.py +1 -0
  8. PyXplore/Extinction/CifReader.py +238 -0
  9. PyXplore/Extinction/Relaxer.py +71 -0
  10. PyXplore/Extinction/XRDpre.py +892 -0
  11. PyXplore/Extinction/__init__.py +1 -0
  12. PyXplore/Extinction/m3gnet/__init__.py +4 -0
  13. PyXplore/Extinction/m3gnet/callbacks.py +26 -0
  14. PyXplore/Extinction/m3gnet/cli.py +112 -0
  15. PyXplore/Extinction/m3gnet/config.py +53 -0
  16. PyXplore/Extinction/m3gnet/graph/__init__.py +31 -0
  17. PyXplore/Extinction/m3gnet/graph/_batch.py +270 -0
  18. PyXplore/Extinction/m3gnet/graph/_compute.py +184 -0
  19. PyXplore/Extinction/m3gnet/graph/_converters.py +224 -0
  20. PyXplore/Extinction/m3gnet/graph/_structure.py +72 -0
  21. PyXplore/Extinction/m3gnet/graph/_types.py +387 -0
  22. PyXplore/Extinction/m3gnet/layers/__init__.py +57 -0
  23. PyXplore/Extinction/m3gnet/layers/_aggregate.py +60 -0
  24. PyXplore/Extinction/m3gnet/layers/_atom.py +107 -0
  25. PyXplore/Extinction/m3gnet/layers/_atom_ref.py +168 -0
  26. PyXplore/Extinction/m3gnet/layers/_base.py +87 -0
  27. PyXplore/Extinction/m3gnet/layers/_basis.py +63 -0
  28. PyXplore/Extinction/m3gnet/layers/_bond.py +232 -0
  29. PyXplore/Extinction/m3gnet/layers/_core.py +246 -0
  30. PyXplore/Extinction/m3gnet/layers/_cutoff.py +36 -0
  31. PyXplore/Extinction/m3gnet/layers/_gn.py +255 -0
  32. PyXplore/Extinction/m3gnet/layers/_readout.py +266 -0
  33. PyXplore/Extinction/m3gnet/layers/_state.py +101 -0
  34. PyXplore/Extinction/m3gnet/layers/_three_body.py +73 -0
  35. PyXplore/Extinction/m3gnet/layers/_two_body.py +48 -0
  36. PyXplore/Extinction/m3gnet/models/__init__.py +16 -0
  37. PyXplore/Extinction/m3gnet/models/_base.py +261 -0
  38. PyXplore/Extinction/m3gnet/models/_dynamics.py +384 -0
  39. PyXplore/Extinction/m3gnet/models/_m3gnet.py +369 -0
  40. PyXplore/Extinction/m3gnet/trainers/__init__.py +5 -0
  41. PyXplore/Extinction/m3gnet/trainers/_metrics.py +33 -0
  42. PyXplore/Extinction/m3gnet/trainers/_potential.py +266 -0
  43. PyXplore/Extinction/m3gnet/trainers/_property.py +288 -0
  44. PyXplore/Extinction/m3gnet/type.py +11 -0
  45. PyXplore/Extinction/m3gnet/utils/__init__.py +49 -0
  46. PyXplore/Extinction/m3gnet/utils/_general.py +73 -0
  47. PyXplore/Extinction/m3gnet/utils/_math.py +386 -0
  48. PyXplore/Extinction/m3gnet/utils/_tf.py +157 -0
  49. PyXplore/Extinction/wyckoff/__init__.py +1 -0
  50. PyXplore/Extinction/wyckoff/wyckoff_dict.py +237 -0
  51. PyXplore/GraphStructure/__init__.py +1 -0
  52. PyXplore/GraphStructure/graph.py +358 -0
  53. PyXplore/Plot/UnitCell.py +125 -0
  54. PyXplore/Plot/__init__.py +1 -0
  55. PyXplore/Refinement/VolumeFractionDertermination.py +223 -0
  56. PyXplore/Refinement/__init__.py +1 -0
  57. PyXplore/StructureOpt/SiteOpt.py +378 -0
  58. PyXplore/StructureOpt/__init__.py +1 -0
  59. PyXplore/WPEM.py +678 -0
  60. PyXplore/WPEMXAS/EXAFS.py +334 -0
  61. PyXplore/WPEMXAS/__init__.py +1 -0
  62. PyXplore/WPEMXAS/fftdemo.ipynb +207 -0
  63. PyXplore/WPEMXPS/XPSEM.py +1010 -0
  64. PyXplore/WPEMXPS/__init__.py +1 -0
  65. PyXplore/XRDSimulation/DiffractionGrometry/__init__.py +1 -0
  66. PyXplore/XRDSimulation/DiffractionGrometry/atom.py +6 -0
  67. PyXplore/XRDSimulation/Simulation.py +628 -0
  68. PyXplore/XRDSimulation/__init__.py +1 -0
  69. PyXplore/__init__.py +11 -0
  70. PyXplore/refs/International_Union_of_Crystallography.pdf +0 -0
  71. PyXplore/refs/WPEM_Manual.pdf +0 -0
  72. PyXplore-2025.7.17.dist-info/METADATA +60 -0
  73. PyXplore-2025.7.17.dist-info/RECORD +75 -0
  74. PyXplore-2025.7.17.dist-info/WHEEL +5 -0
  75. 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
+