pyIntersecT 0.0.1__py2.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.
pyIntersecT/IntersecT.py
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
#%%
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import matplotlib.pyplot as plt
|
|
7
|
+
import matplotlib.colors as colors
|
|
8
|
+
from tkinter import filedialog
|
|
9
|
+
|
|
10
|
+
class QualityFactorAnalysis:
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.data = pd.DataFrame()
|
|
13
|
+
self.labels = []
|
|
14
|
+
self.x = []
|
|
15
|
+
self.y = []
|
|
16
|
+
self.labels_new = []
|
|
17
|
+
self.phase_id = []
|
|
18
|
+
self.apfu_name = []
|
|
19
|
+
self.apfu_obs = []
|
|
20
|
+
self.obs_err = []
|
|
21
|
+
self.analysis_type = ''
|
|
22
|
+
self.color_scheme = 'viridis'
|
|
23
|
+
self.phase_name = []
|
|
24
|
+
self.output_dir = ''
|
|
25
|
+
self.min_redchi2 = []
|
|
26
|
+
|
|
27
|
+
def set_output_directory(self):
|
|
28
|
+
"""Prompt user to select an output directory."""
|
|
29
|
+
print("Please select an output directory.")
|
|
30
|
+
|
|
31
|
+
self.output_dir = filedialog.askdirectory()
|
|
32
|
+
print("The output directory is: ", self.output_dir)
|
|
33
|
+
|
|
34
|
+
def import_output_perplex(self):
|
|
35
|
+
"""Import data from a perplex tab file and process it."""
|
|
36
|
+
print("Please select a tab file from perplex.")
|
|
37
|
+
|
|
38
|
+
filename = filedialog.askopenfilename()
|
|
39
|
+
self.data = pd.read_csv(filename, sep=r'\s+', header=0, skiprows=12)
|
|
40
|
+
print("The data file is: ", filename)
|
|
41
|
+
|
|
42
|
+
self.labels = np.array(self.data.columns) # Convert Index to a list
|
|
43
|
+
self.x, self.y = np.array(self.data[self.labels[0]]), np.array(self.data[self.labels[1]])
|
|
44
|
+
|
|
45
|
+
# Convert units if necessary
|
|
46
|
+
if self.labels[0] == "T(K)":
|
|
47
|
+
self.x -= 273
|
|
48
|
+
self.labels[0] = "T(°C)"
|
|
49
|
+
elif self.labels[0] == "P(bar)":
|
|
50
|
+
self.x /= 10000
|
|
51
|
+
self.labels[0] = "P(GPa)"
|
|
52
|
+
|
|
53
|
+
if self.labels[1] == "P(bar)":
|
|
54
|
+
self.y /= 10000
|
|
55
|
+
self.labels[1] = "P(GPa)"
|
|
56
|
+
elif self.labels[1] == "T(K)":
|
|
57
|
+
self.y -= 273
|
|
58
|
+
self.labels[1] = "T(°C)"
|
|
59
|
+
|
|
60
|
+
print("The werami variables are: ", self.labels)
|
|
61
|
+
print("The", self.labels[0], "range is:", self.x)
|
|
62
|
+
print("The", self.labels[1], "range is:", self.y)
|
|
63
|
+
|
|
64
|
+
self.labels_new = np.array([''.join([j for j in label if not j.isdigit()]) for label in self.labels[2:]])
|
|
65
|
+
|
|
66
|
+
id = 0
|
|
67
|
+
self.phase_id = np.array([id if label == self.labels_new[i-1] else (id := id + 1) for i, label in enumerate(self.labels_new)])
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def import_analytical_compo(self):
|
|
71
|
+
"""Import composition data from a file."""
|
|
72
|
+
|
|
73
|
+
print("Please select a text file containing analyses.")
|
|
74
|
+
|
|
75
|
+
filename = filedialog.askopenfilename()
|
|
76
|
+
input_data = pd.read_csv(filename, sep='\t', header=None, comment='#')
|
|
77
|
+
print("The composition file is: ", filename)
|
|
78
|
+
|
|
79
|
+
arrays = input_data.values
|
|
80
|
+
|
|
81
|
+
self.apfu_name = np.array([str(i) for i in arrays[0] if str(i) != 'nan'])
|
|
82
|
+
self.apfu_obs = np.array([float(i) for i in arrays[1] if str(i) != 'nan'])
|
|
83
|
+
self.obs_err = np.array([float(i) if str(i) != '-' else '-' for i in arrays[2] if str(i) != 'nan'])
|
|
84
|
+
|
|
85
|
+
# If self.obs_err contains only '-', set it to an empty array
|
|
86
|
+
if np.all(self.obs_err == '-'):
|
|
87
|
+
self.obs_err = np.array([]) # Set to empty array if all values are '-'
|
|
88
|
+
else:
|
|
89
|
+
# Replace valid numeric entries below 0.01 with 0.01
|
|
90
|
+
self.obs_err = np.where((self.obs_err != '-') & (self.obs_err < 0.01), 0.01, self.obs_err)
|
|
91
|
+
|
|
92
|
+
self.analysis_type = ''.join([str(i) for i in arrays[3] if str(i) != 'nan'])
|
|
93
|
+
self.phase_name = np.array([str(i) for i in arrays[5] if str(i) != 'nan'])
|
|
94
|
+
self.color_scheme = ''.join([str(i) for i in arrays[7] if str(i) != 'nan'])
|
|
95
|
+
|
|
96
|
+
print("The input variables are:", self.apfu_name)
|
|
97
|
+
print("The input compositions are:", self.apfu_obs)
|
|
98
|
+
print("The input uncertainties are:", self.obs_err)
|
|
99
|
+
print("The selected analysis type is:", self.analysis_type)
|
|
100
|
+
print("The phase names are:", self.phase_name)
|
|
101
|
+
print("The color scheme is:", self.color_scheme)
|
|
102
|
+
|
|
103
|
+
if len(self.obs_err) == 0:
|
|
104
|
+
self.obs_err = self.calc_obs_err(self.apfu_obs)
|
|
105
|
+
|
|
106
|
+
def calc_obs_err(self, apfu_obs):
|
|
107
|
+
"""Calculate observation errors based on analysis type."""
|
|
108
|
+
if self.analysis_type == 'EDS':
|
|
109
|
+
calc_err = 0.0703 * (apfu_obs**0.3574)
|
|
110
|
+
min_err, max_err = 0.01, 0.1
|
|
111
|
+
elif self.analysis_type == 'WDS map':
|
|
112
|
+
calc_err = 0.0434 * (apfu_obs**0.3451)
|
|
113
|
+
min_err, max_err = 0.005, 0.05
|
|
114
|
+
elif self.analysis_type == 'WDS spot':
|
|
115
|
+
calc_err = 0.023 * (apfu_obs**0.2772)
|
|
116
|
+
min_err, max_err = 0.005, 0.05
|
|
117
|
+
else:
|
|
118
|
+
print('Please enter a valid analysis type (EDS, WDS map, WDS spot)')
|
|
119
|
+
sys.exit()
|
|
120
|
+
|
|
121
|
+
calc_err = np.clip(calc_err, min_err, max_err)
|
|
122
|
+
print('The calculated uncertainties are: ', calc_err)
|
|
123
|
+
return calc_err
|
|
124
|
+
|
|
125
|
+
def Q_elem(self, apfu_obs, model, obs_err):
|
|
126
|
+
"""Calculate quality factor for each element."""
|
|
127
|
+
factor_min = 1
|
|
128
|
+
factor_max = 6
|
|
129
|
+
|
|
130
|
+
obs_err = np.where(obs_err < 0.01, 0.01, obs_err)
|
|
131
|
+
diff = np.abs(apfu_obs - model)
|
|
132
|
+
num = np.clip(diff - obs_err / factor_min, 0, factor_max * obs_err)
|
|
133
|
+
|
|
134
|
+
Qcmp_elem = 100 * np.abs(1 - num / (factor_max * obs_err))**(model + 1)
|
|
135
|
+
return Qcmp_elem
|
|
136
|
+
|
|
137
|
+
def Q_phase(self, apfu_obs, model, obs_err):
|
|
138
|
+
"""Calculate quality factor for each phase."""
|
|
139
|
+
|
|
140
|
+
factor_min = 1
|
|
141
|
+
factor_max = 6
|
|
142
|
+
|
|
143
|
+
obs_err = np.where(obs_err < 0.01, 0.01, obs_err)
|
|
144
|
+
diff = np.abs(apfu_obs - model)
|
|
145
|
+
num = np.clip(diff - obs_err / factor_min, 0, factor_max * obs_err)
|
|
146
|
+
|
|
147
|
+
Qcmp_elem = np.abs(1 - num / (factor_max * obs_err))**(model + 1)
|
|
148
|
+
Qcmp_phase = np.sum(Qcmp_elem) / np.size(Qcmp_elem) * 100
|
|
149
|
+
return Qcmp_phase
|
|
150
|
+
|
|
151
|
+
def chi2(self, apfu_obs, model, obs_err):
|
|
152
|
+
"""Calculate chi-squared value."""
|
|
153
|
+
return np.sum((apfu_obs - model)**2 / obs_err**2)
|
|
154
|
+
|
|
155
|
+
def red_chi2(self, apfu_obs, model, obs_err, f):
|
|
156
|
+
"""Calculate reduced chi-squared value."""
|
|
157
|
+
return self.chi2(apfu_obs, model, obs_err) / (f - 1)
|
|
158
|
+
|
|
159
|
+
def norm_weight(self, weight):
|
|
160
|
+
"""Normalize the weight array by dividing each element by the sum of the weights."""
|
|
161
|
+
weight_norm = weight / np.sum(weight)
|
|
162
|
+
return weight_norm
|
|
163
|
+
|
|
164
|
+
def Q_tot(self, Qcmp_tot, weight_norm):
|
|
165
|
+
"""Calculate total quality factor."""
|
|
166
|
+
return np.sum(Qcmp_tot * weight_norm)
|
|
167
|
+
|
|
168
|
+
def plot_elem(self, Qcmp, i):
|
|
169
|
+
"""Plot quality factor for each element."""
|
|
170
|
+
Qcmp_2D = np.reshape(Qcmp, (len(np.unique(self.y)), len(np.unique(self.x))))
|
|
171
|
+
plt.imshow(Qcmp_2D, cmap=self.color_scheme, aspect='auto', origin='lower', extent=[min(self.x), max(self.x), min(self.y), max(self.y)])
|
|
172
|
+
plt.colorbar()
|
|
173
|
+
plt.title('Quality factor for ' + self.apfu_name[i])
|
|
174
|
+
plt.xlabel(self.labels[0])
|
|
175
|
+
plt.ylabel(self.labels[1])
|
|
176
|
+
plt.clim(0, 100)
|
|
177
|
+
|
|
178
|
+
print('The maximum value of the quality factor for ' + self.apfu_name[i] + ' is: ', np.nanmax(Qcmp_2D))
|
|
179
|
+
contoured = plt.contour(Qcmp_2D, levels=np.arange(0, 100, 10), colors="white", linewidths=0.5, origin="lower", extent=[min(self.x), max(self.x), min(self.y), max(self.y)])
|
|
180
|
+
plt.clabel(contoured, inline=True, fontsize=10, fmt='%1.0f')
|
|
181
|
+
|
|
182
|
+
# Save plot as PDF
|
|
183
|
+
output_path = os.path.join(self.output_dir)
|
|
184
|
+
os.makedirs(output_path, exist_ok=True)
|
|
185
|
+
plt.savefig(os.path.join(output_path, "Qcmp_" + self.apfu_name[i] + '.pdf'), format='pdf')
|
|
186
|
+
|
|
187
|
+
plt.show()
|
|
188
|
+
plt.close()
|
|
189
|
+
|
|
190
|
+
def plot_phase(self, Qcmp, i):
|
|
191
|
+
"""Plot quality factor for each phase."""
|
|
192
|
+
Qcmp_2D = np.reshape(Qcmp, (len(np.unique(self.y)), len(np.unique(self.x))))
|
|
193
|
+
plt.imshow(Qcmp_2D, cmap=self.color_scheme, aspect='auto', origin='lower', extent=[min(self.x), max(self.x), min(self.y), max(self.y)])
|
|
194
|
+
plt.colorbar()
|
|
195
|
+
plt.title('Quality factor for ' + self.phase_name[i-1])
|
|
196
|
+
plt.xlabel(self.labels[0])
|
|
197
|
+
plt.ylabel(self.labels[1])
|
|
198
|
+
plt.clim(0, 100)
|
|
199
|
+
|
|
200
|
+
print('The maximum value of the quality factor for ' + self.phase_name[i-1] + ' is: ', np.nanmax(Qcmp_2D))
|
|
201
|
+
contoured = plt.contour(Qcmp_2D, levels=np.arange(0, 100, 10), colors="white", linewidths=0.5, origin="lower", extent=[min(self.x), max(self.x), min(self.y), max(self.y)])
|
|
202
|
+
plt.clabel(contoured, inline=True, fontsize=10, fmt='%1.0f')
|
|
203
|
+
|
|
204
|
+
if np.nanmax(Qcmp_2D) < 100:
|
|
205
|
+
n_p = np.count_nonzero(self.y == self.y[0])
|
|
206
|
+
max_Qcmp = np.where(Qcmp_2D == np.nanmax(Qcmp_2D))
|
|
207
|
+
max_Qcmp_y = self.y[max_Qcmp[0]*n_p]
|
|
208
|
+
max_Qcmp_x = self.x[max_Qcmp[1]]
|
|
209
|
+
print('The ' + self.labels[0] + ' and ' + self.labels[1] + ' position of the maximum Qcmp of ', self.phase_name[i-1],' is: ', max_Qcmp_x, ', ', max_Qcmp_y)
|
|
210
|
+
|
|
211
|
+
# Save plot as PDF
|
|
212
|
+
output_path = os.path.join(self.output_dir)
|
|
213
|
+
os.makedirs(output_path, exist_ok=True)
|
|
214
|
+
plt.savefig(os.path.join(output_path, "Qcmp_" + self.phase_name[i-1] + '.pdf'), format='pdf')
|
|
215
|
+
|
|
216
|
+
plt.show()
|
|
217
|
+
plt.close()
|
|
218
|
+
|
|
219
|
+
def plot_tot(self, Qcmp, title):
|
|
220
|
+
Qcmp_2D = np.reshape(Qcmp, (len(np.unique(self.y)), len(np.unique(self.x))))
|
|
221
|
+
plt.imshow(Qcmp_2D, cmap='viridis', aspect='auto', origin='lower', extent=[min(self.x), max(self.x), min(self.y), max(self.y)])
|
|
222
|
+
plt.colorbar()
|
|
223
|
+
plt.title(title + ' total quality factor')
|
|
224
|
+
plt.xlabel(self.labels[0])
|
|
225
|
+
plt.ylabel(self.labels[1])
|
|
226
|
+
plt.clim(0, 100)
|
|
227
|
+
print('The maximum value of the total quality factor is: ', np.nanmax(Qcmp_2D))
|
|
228
|
+
|
|
229
|
+
contoured = plt.contour(Qcmp_2D, levels=np.arange(0, 110, 10), colors="white", linewidths=0.5, origin="lower", extent=[min(self.x), max(self.x), min(self.y), max(self.y)])
|
|
230
|
+
plt.clabel(contoured, inline=True, fontsize=10, fmt='%1.0f')
|
|
231
|
+
|
|
232
|
+
max_Qcmp = np.where(Qcmp_2D == np.nanmax(Qcmp_2D))
|
|
233
|
+
n_p = np.count_nonzero(self.y == self.y[0])
|
|
234
|
+
max_Qcmp_y = self.y[max_Qcmp[0]*n_p]
|
|
235
|
+
max_Qcmp_x = self.x[max_Qcmp[1]]
|
|
236
|
+
max_Qcmp_x = np.mean(max_Qcmp_x)
|
|
237
|
+
max_Qcmp_y = np.mean(max_Qcmp_y)
|
|
238
|
+
print('The ' + self.labels[0] + ' and ' + self.labels[1] + ' position of the maximum total Qcmp is: ', max_Qcmp_x, ', ', max_Qcmp_y)
|
|
239
|
+
|
|
240
|
+
plt.plot(max_Qcmp_x, max_Qcmp_y, "ro", markersize=2)
|
|
241
|
+
os.makedirs(self.output_dir, exist_ok=True)
|
|
242
|
+
plt.savefig(os.path.join(self.output_dir, title + "_Qcmp_tot.pdf"), format='pdf')
|
|
243
|
+
plt.show()
|
|
244
|
+
plt.close()
|
|
245
|
+
|
|
246
|
+
def plot_redchi2_phase(self, redchi2, i, title):
|
|
247
|
+
redchi2_2D = np.reshape(redchi2, (len(np.unique(self.y)), len(np.unique(self.x))))
|
|
248
|
+
plt.imshow(redchi2_2D, cmap='viridis', aspect='auto', origin='lower', extent=[min(self.x), max(self.x), min(self.y), max(self.y)], norm=colors.LogNorm())
|
|
249
|
+
plt.colorbar()
|
|
250
|
+
plt.title(title + self.phase_name[i-1])
|
|
251
|
+
plt.xlabel(self.labels[0])
|
|
252
|
+
plt.ylabel(self.labels[1])
|
|
253
|
+
|
|
254
|
+
min_redchi2 = np.nanmin(redchi2_2D)
|
|
255
|
+
max_redchi2 = np.nanmax(redchi2_2D)
|
|
256
|
+
step = (max_redchi2 - min_redchi2) / 10
|
|
257
|
+
print('The minimum reduced χ2 value for ', self.phase_name[i-1], ' is: ', min_redchi2)
|
|
258
|
+
|
|
259
|
+
if min_redchi2 <= 1:
|
|
260
|
+
contoured = plt.contour(redchi2_2D, levels=np.arange(1, max_redchi2, step), colors="white", linewidths=0.5, origin="lower", extent=[min(self.x), max(self.x), min(self.y), max(self.y)], norm=colors.LogNorm())
|
|
261
|
+
else:
|
|
262
|
+
contoured = plt.contour(redchi2_2D, levels=np.arange(np.round(min_redchi2, decimals=1)+0.1, max_redchi2, step), colors="white", linewidths=0.5, origin="lower", extent=[min(self.x), max(self.x), min(self.y), max(self.y)], norm=colors.LogNorm())
|
|
263
|
+
|
|
264
|
+
plt.clabel(contoured, inline=True, fontsize=10, fmt='%1.1f')
|
|
265
|
+
title = title.replace(" ", "_")
|
|
266
|
+
os.makedirs(self.output_dir, exist_ok=True)
|
|
267
|
+
plt.savefig(os.path.join(self.output_dir, title + self.phase_name[i-1] + '.pdf'), format='pdf')
|
|
268
|
+
plt.show()
|
|
269
|
+
plt.close()
|
|
270
|
+
|
|
271
|
+
return min_redchi2
|
|
272
|
+
|
|
273
|
+
def plot_redchi2_tot(self, redchi2):
|
|
274
|
+
redchi2_2D = np.reshape(redchi2, (len(np.unique(self.y)), len(np.unique(self.x))))
|
|
275
|
+
plt.imshow(redchi2_2D, cmap='viridis', aspect='auto', origin='lower', extent=[min(self.x), max(self.x), min(self.y), max(self.y)], norm=colors.LogNorm())
|
|
276
|
+
plt.colorbar()
|
|
277
|
+
plt.title('Total reduced χ2')
|
|
278
|
+
plt.xlabel(self.labels[0])
|
|
279
|
+
plt.ylabel(self.labels[1])
|
|
280
|
+
|
|
281
|
+
min_redchi2 = np.nanmin(redchi2_2D)
|
|
282
|
+
max_redchi2 = np.nanmax(redchi2_2D)
|
|
283
|
+
step = (max_redchi2 - min_redchi2) / 10
|
|
284
|
+
|
|
285
|
+
if min_redchi2 <= 1:
|
|
286
|
+
contoured = plt.contour(redchi2_2D, levels=np.arange(1, max_redchi2, step), colors="white", linewidths=0.5, origin="lower", extent=[min(self.x), max(self.x), min(self.y), max(self.y)], norm=colors.LogNorm())
|
|
287
|
+
else:
|
|
288
|
+
contoured = plt.contour(redchi2_2D, levels=np.arange(np.round(min_redchi2, decimals=1)+0.1, max_redchi2, step), colors="white", linewidths=0.5, origin="lower", extent=[min(self.x), max(self.x), min(self.y), max(self.y)], norm=colors.LogNorm())
|
|
289
|
+
|
|
290
|
+
plt.clabel(contoured, inline=True, fontsize=10, fmt='%1.1f')
|
|
291
|
+
print('The minimum value of the total reduced χ2 is: ', min_redchi2)
|
|
292
|
+
|
|
293
|
+
n_p = np.count_nonzero(self.y == self.y[0])
|
|
294
|
+
min_redchi2_pos = np.where(redchi2_2D == np.nanmin(redchi2_2D))
|
|
295
|
+
min_redchi2_y = self.y[min_redchi2_pos[0]*n_p]
|
|
296
|
+
min_redchi2_x = self.x[min_redchi2_pos[1]]
|
|
297
|
+
print('The ' + self.labels[0] + ' and ' + self.labels[1] + ' position of the minimum total reduced χ2 is: ', min_redchi2_x , ', ', min_redchi2_y)
|
|
298
|
+
|
|
299
|
+
os.makedirs(self.output_dir, exist_ok=True)
|
|
300
|
+
plt.savefig(os.path.join(self.output_dir, "redχ2_tot.pdf"), format='pdf')
|
|
301
|
+
plt.show()
|
|
302
|
+
plt.close()
|
|
303
|
+
|
|
304
|
+
return min_redchi2
|
|
305
|
+
|
|
306
|
+
def Qcmp_elem(self):
|
|
307
|
+
Qcmp_elem = np.empty(len(self.data[self.labels[2]]))
|
|
308
|
+
for i in range(len(self.labels[2:])):
|
|
309
|
+
Model_elem = np.array(self.data[self.labels[i+2]])
|
|
310
|
+
for j in range(len(Model_elem)):
|
|
311
|
+
Qcmp_elem[j] = self.Q_elem(self.apfu_obs[i], Model_elem[j], self.obs_err[i])
|
|
312
|
+
self.plot_elem(Qcmp_elem, i)
|
|
313
|
+
|
|
314
|
+
def Qcmp_phase(self):
|
|
315
|
+
phase_idx = []
|
|
316
|
+
Qcmp_phase_tot = np.empty((len(self.y), len(np.unique(self.phase_id))))
|
|
317
|
+
|
|
318
|
+
for i in np.unique(self.phase_id):
|
|
319
|
+
idx = np.where(self.phase_id == i)[0]+2
|
|
320
|
+
phase_idx.append(idx)
|
|
321
|
+
#print(idx)
|
|
322
|
+
Model_phase = np.array(self.data[self.labels[idx]])
|
|
323
|
+
apfu_obs_idx = self.apfu_obs[idx-2]
|
|
324
|
+
obs_err_idx = self.obs_err[idx-2]
|
|
325
|
+
idx_phase = np.arange(len(idx))
|
|
326
|
+
Qcmp_phase = np.empty(len(Model_phase))
|
|
327
|
+
|
|
328
|
+
for j in range(len(Model_phase)):
|
|
329
|
+
Qcmp_phase[j] = self.Q_phase(apfu_obs_idx, Model_phase[j, idx_phase], obs_err_idx)
|
|
330
|
+
|
|
331
|
+
self.plot_phase(Qcmp_phase, i)
|
|
332
|
+
Qcmp_phase_tot[:, i-1] = Qcmp_phase
|
|
333
|
+
|
|
334
|
+
return Qcmp_phase_tot
|
|
335
|
+
|
|
336
|
+
def redchi2_phase(self):
|
|
337
|
+
phase_idx = []
|
|
338
|
+
redchi2_phase_tot = np.empty((len(self.y), len(np.unique(self.phase_id))))
|
|
339
|
+
|
|
340
|
+
for i in np.unique(self.phase_id):
|
|
341
|
+
idx = np.where(self.phase_id == i)[0]+2
|
|
342
|
+
phase_idx.append(idx)
|
|
343
|
+
|
|
344
|
+
f = len(idx)
|
|
345
|
+
Model_phase = np.array(self.data[self.labels[idx]])
|
|
346
|
+
apfu_obs_idx = self.apfu_obs[idx-2]
|
|
347
|
+
obs_err_idx = self.obs_err[idx-2]
|
|
348
|
+
idx_phase = np.arange(len(idx))
|
|
349
|
+
redchi2_phase = np.empty(len(Model_phase))
|
|
350
|
+
|
|
351
|
+
if f > 2:
|
|
352
|
+
for j in range(len(Model_phase)):
|
|
353
|
+
redchi2_phase[j] = self.red_chi2(apfu_obs_idx, Model_phase[j, idx_phase], obs_err_idx, f)
|
|
354
|
+
title = 'Reduced χ2 '
|
|
355
|
+
min_redchi2_phase = self.plot_redchi2_phase(redchi2_phase, i, title)
|
|
356
|
+
print("The number of elements in " + self.phase_name[i-1] + " is:", f)
|
|
357
|
+
|
|
358
|
+
else:
|
|
359
|
+
for j in range(len(Model_phase)):
|
|
360
|
+
redchi2_phase[j] = self.chi2(apfu_obs_idx, Model_phase[j, idx_phase], obs_err_idx)
|
|
361
|
+
title = 'χ2 '
|
|
362
|
+
min_redchi2_phase = self.plot_redchi2_phase(redchi2_phase, i, title)
|
|
363
|
+
print("The number of elements in " + self.phase_name[i-1] + " is:", f)
|
|
364
|
+
print('The minimum value of the χ2 for ' + self.phase_name[i-1] + ' is: ', min_redchi2_phase)
|
|
365
|
+
min_redchi2_phase = 1 + min_redchi2_phase
|
|
366
|
+
if f == 1:
|
|
367
|
+
print('WARNING: The number of elements in ' + self.phase_name[i-1] + ' is 1, the χ2 may not be accurate')
|
|
368
|
+
|
|
369
|
+
redchi2_phase_tot[:, i-1] = redchi2_phase
|
|
370
|
+
self.min_redchi2 = np.append(self.min_redchi2, min_redchi2_phase)
|
|
371
|
+
|
|
372
|
+
return redchi2_phase_tot
|
|
373
|
+
|
|
374
|
+
def redchi2_tot(self):
|
|
375
|
+
f = len(self.labels) - 2
|
|
376
|
+
redchi2_tot = np.empty(len(self.data[self.labels[2]]))
|
|
377
|
+
|
|
378
|
+
for i in range(len(self.data[self.labels[2]])):
|
|
379
|
+
Model_tot = np.array(self.data[self.labels[2:]])
|
|
380
|
+
apfu_obs_tot = self.apfu_obs
|
|
381
|
+
obs_err_tot = self.obs_err
|
|
382
|
+
redchi2_tot[i] = self.red_chi2(apfu_obs_tot, Model_tot[i], obs_err_tot, f)
|
|
383
|
+
|
|
384
|
+
min_redchi2_tot = self.plot_redchi2_tot(redchi2_tot)
|
|
385
|
+
return min_redchi2_tot
|
|
386
|
+
|
|
387
|
+
def Qcmp_tot(self, Qcmp_phase_tot, redchi2_phase_tot):
|
|
388
|
+
"""Calculate the total quality factor (Qcmp_tot) for the modelled composition."""
|
|
389
|
+
|
|
390
|
+
# Ensure phase_id exists and is part of self
|
|
391
|
+
if self.phase_id is None:
|
|
392
|
+
raise ValueError("phase_id is not set. Ensure it is initialized.")
|
|
393
|
+
|
|
394
|
+
# All phases have the same weight
|
|
395
|
+
weight = np.ones(len(np.unique(self.phase_id)))
|
|
396
|
+
|
|
397
|
+
# Normalize the weight
|
|
398
|
+
weight_norm = self.norm_weight(weight)
|
|
399
|
+
|
|
400
|
+
# Calculate the quality factor for each row of mod_elem
|
|
401
|
+
Qcmp_allphases = np.empty(len(Qcmp_phase_tot[:, 0]))
|
|
402
|
+
|
|
403
|
+
# Calculate the total quality factor for each row of the modelled composition
|
|
404
|
+
for i in range(len(Qcmp_phase_tot[:, 0])):
|
|
405
|
+
Qcmp_allphases[i] = self.Q_tot(Qcmp_phase_tot[i, :], weight_norm)
|
|
406
|
+
|
|
407
|
+
# Find the position of the maximum value of the quality factor
|
|
408
|
+
max_Qcmp = np.where(Qcmp_allphases == np.nanmax(Qcmp_allphases))
|
|
409
|
+
|
|
410
|
+
# Accessing the redchi2_phase at the location of the maximum Qcmp_allphases_weight
|
|
411
|
+
Qcmpmax_redchi2_value = redchi2_phase_tot[max_Qcmp[0][0]]
|
|
412
|
+
|
|
413
|
+
print("The reduced χ2 values for the phases at the maximum total Qcmp is:", Qcmpmax_redchi2_value)
|
|
414
|
+
|
|
415
|
+
# Plot the results
|
|
416
|
+
self.plot_tot(Qcmp_allphases, "Unweighted")
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def Qcmp_tot_weight(self, Qcmp_phase_tot, redchi2_phase_tot):
|
|
420
|
+
"""Calculate the weighted total quality factor (Qcmp_tot_weight) for the modelled composition."""
|
|
421
|
+
|
|
422
|
+
# Calculate weight based on the minimum reduced χ2 value of each phase
|
|
423
|
+
self.min_redchi2[self.min_redchi2 < 1] = 1
|
|
424
|
+
weight = 1 / self.min_redchi2
|
|
425
|
+
print('The weight is: ', weight)
|
|
426
|
+
|
|
427
|
+
# Normalize the weight
|
|
428
|
+
weight_norm = self.norm_weight(weight)
|
|
429
|
+
print('The normalized weight fraction is: ', weight_norm)
|
|
430
|
+
|
|
431
|
+
# Calculate the quality factor for each row of mod_elem
|
|
432
|
+
Qcmp_allphases_weight = np.empty(len(Qcmp_phase_tot))
|
|
433
|
+
|
|
434
|
+
# Calculate the quality factor total for each row of the modelled composition
|
|
435
|
+
for i in range(len(Qcmp_phase_tot)):
|
|
436
|
+
Qcmp_allphases_weight[i] = self.Q_tot(Qcmp_phase_tot[i, :], weight_norm)
|
|
437
|
+
|
|
438
|
+
# Find the position of the maximum value of the quality factor
|
|
439
|
+
max_Qcmp = np.where(Qcmp_allphases_weight == np.nanmax(Qcmp_allphases_weight))
|
|
440
|
+
|
|
441
|
+
# Accessing the redchi2_phase at the location of the maximum Qcmp_allphases_weight
|
|
442
|
+
Qcmpmax_redchi2_value = redchi2_phase_tot[max_Qcmp[0][0]]
|
|
443
|
+
print("The reduced χ2 values for the phases at the maximum total Qcmp are:", Qcmpmax_redchi2_value)
|
|
444
|
+
|
|
445
|
+
# Plot the results
|
|
446
|
+
self.plot_tot(Qcmp_allphases_weight, "Weighted")
|
|
447
|
+
|
|
448
|
+
return Qcmp_allphases_weight
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
#! this allows to test the file only if it is run as the main file
|
|
452
|
+
if __name__ == "__main__":
|
|
453
|
+
QFA = QualityFactorAnalysis()
|
|
454
|
+
QFA.run_analysis(0)
|
|
Binary file
|
pyIntersecT/__init__.py
ADDED
|
File without changes
|