calphy 1.3.13__py3-none-any.whl → 1.4.3__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.
- calphy/__init__.py +1 -1
- calphy/alchemy.py +66 -8
- calphy/clitools.py +6 -0
- calphy/composition_transformation.py +63 -9
- calphy/helpers.py +6 -2
- calphy/input.py +478 -245
- calphy/kernel.py +3 -1
- calphy/liquid.py +199 -118
- calphy/phase.py +810 -381
- calphy/phase_diagram.py +503 -26
- calphy/postprocessing.py +192 -4
- calphy/routines.py +13 -2
- calphy/scheduler.py +2 -0
- calphy/solid.py +36 -1
- {calphy-1.3.13.dist-info → calphy-1.4.3.dist-info}/METADATA +14 -2
- calphy-1.4.3.dist-info/RECORD +25 -0
- {calphy-1.3.13.dist-info → calphy-1.4.3.dist-info}/WHEEL +1 -1
- {calphy-1.3.13.dist-info → calphy-1.4.3.dist-info}/entry_points.txt +1 -0
- calphy-1.3.13.dist-info/RECORD +0 -25
- {calphy-1.3.13.dist-info → calphy-1.4.3.dist-info/licenses}/LICENSE +0 -0
- {calphy-1.3.13.dist-info → calphy-1.4.3.dist-info}/top_level.txt +0 -0
calphy/phase_diagram.py
CHANGED
|
@@ -4,18 +4,469 @@ import pandas as pd
|
|
|
4
4
|
import matplotlib.pyplot as plt
|
|
5
5
|
import warnings
|
|
6
6
|
import itertools
|
|
7
|
+
from itertools import combinations
|
|
7
8
|
import math
|
|
9
|
+
import copy
|
|
10
|
+
import os
|
|
11
|
+
from calphy.composition_transformation import CompositionTransformation
|
|
12
|
+
import yaml
|
|
13
|
+
import matplotlib.patches as mpatches
|
|
8
14
|
|
|
9
15
|
from calphy.integrators import kb
|
|
10
16
|
|
|
11
17
|
from scipy.spatial import ConvexHull
|
|
12
18
|
from scipy.interpolate import splrep, splev
|
|
19
|
+
from scipy.optimize import curve_fit
|
|
20
|
+
|
|
13
21
|
|
|
14
22
|
colors = ['#a6cee3','#1f78b4','#b2df8a',
|
|
15
23
|
'#33a02c','#fb9a99','#e31a1c',
|
|
16
24
|
'#fdbf6f','#ff7f00','#cab2d6',
|
|
17
25
|
'#6a3d9a','#ffff99','#b15928']
|
|
18
26
|
|
|
27
|
+
matcolors = {
|
|
28
|
+
"amber": {
|
|
29
|
+
50 : '#fff8e1',
|
|
30
|
+
100 : '#ffecb3',
|
|
31
|
+
200 : '#ffe082',
|
|
32
|
+
300 : '#ffd54f',
|
|
33
|
+
400 : '#ffca28',
|
|
34
|
+
500 : '#ffc107',
|
|
35
|
+
600 : '#ffb300',
|
|
36
|
+
700 : '#ffa000',
|
|
37
|
+
800 : '#ff8f00',
|
|
38
|
+
900 : '#ff6f00',
|
|
39
|
+
},
|
|
40
|
+
"blue_grey": {
|
|
41
|
+
50 : '#ECEFF1',
|
|
42
|
+
100 : '#CFD8DC',
|
|
43
|
+
200 : '#B0BEC5',
|
|
44
|
+
300 : '#90A4AE',
|
|
45
|
+
400 : '#78909C',
|
|
46
|
+
500 : '#607D8B',
|
|
47
|
+
600 : '#546E7A',
|
|
48
|
+
700 : '#455A64',
|
|
49
|
+
800 : '#37474F',
|
|
50
|
+
900 : '#263238',
|
|
51
|
+
},
|
|
52
|
+
"blue": {
|
|
53
|
+
50 : '#E3F2FD',
|
|
54
|
+
100 : '#BBDEFB',
|
|
55
|
+
200 : '#90CAF9',
|
|
56
|
+
300 : '#64B5F6',
|
|
57
|
+
400 : '#42A5F5',
|
|
58
|
+
500 : '#2196F3',
|
|
59
|
+
600 : '#1E88E5',
|
|
60
|
+
700 : '#1976D2',
|
|
61
|
+
800 : '#1565C0',
|
|
62
|
+
900 : '#0D47A1',
|
|
63
|
+
},
|
|
64
|
+
"brown": {
|
|
65
|
+
50 : '#EFEBE9',
|
|
66
|
+
100 : '#D7CCC8',
|
|
67
|
+
200 : '#BCAAA4',
|
|
68
|
+
300 : '#A1887F',
|
|
69
|
+
400 : '#8D6E63',
|
|
70
|
+
500 : '#795548',
|
|
71
|
+
600 : '#6D4C41',
|
|
72
|
+
700 : '#5D4037',
|
|
73
|
+
800 : '#4E342E',
|
|
74
|
+
900 : '#3E2723',
|
|
75
|
+
},
|
|
76
|
+
"cyan": {
|
|
77
|
+
50 : '#E0F7FA',
|
|
78
|
+
100 : '#B2EBF2',
|
|
79
|
+
200 : '#80DEEA',
|
|
80
|
+
300 : '#4DD0E1',
|
|
81
|
+
400 : '#26C6DA',
|
|
82
|
+
500 : '#00BCD4',
|
|
83
|
+
600 : '#00ACC1',
|
|
84
|
+
700 : '#0097A7',
|
|
85
|
+
800 : '#00838F',
|
|
86
|
+
900 : '#006064',
|
|
87
|
+
},
|
|
88
|
+
"deep_orange": {
|
|
89
|
+
50 : '#FBE9E7',
|
|
90
|
+
100 : '#FFCCBC',
|
|
91
|
+
200 : '#FFAB91',
|
|
92
|
+
300 : '#FF8A65',
|
|
93
|
+
400 : '#FF7043',
|
|
94
|
+
500 : '#FF5722',
|
|
95
|
+
600 : '#F4511E',
|
|
96
|
+
700 : '#E64A19',
|
|
97
|
+
800 : '#D84315',
|
|
98
|
+
900 : '#BF360C',
|
|
99
|
+
},
|
|
100
|
+
"deep_purple": {
|
|
101
|
+
50 : '#EDE7F6',
|
|
102
|
+
100 : '#D1C4E9',
|
|
103
|
+
200 : '#B39DDB',
|
|
104
|
+
300 : '#9575CD',
|
|
105
|
+
400 : '#7E57C2',
|
|
106
|
+
500 : '#673AB7',
|
|
107
|
+
600 : '#5E35B1',
|
|
108
|
+
700 : '#512DA8',
|
|
109
|
+
800 : '#4527A0',
|
|
110
|
+
900 : '#311B92',
|
|
111
|
+
},
|
|
112
|
+
"green": {
|
|
113
|
+
50 : '#E8F5E9',
|
|
114
|
+
100 : '#C8E6C9',
|
|
115
|
+
200 : '#A5D6A7',
|
|
116
|
+
300 : '#81C784',
|
|
117
|
+
400 : '#66BB6A',
|
|
118
|
+
500 : '#4CAF50',
|
|
119
|
+
600 : '#43A047',
|
|
120
|
+
700 : '#388E3C',
|
|
121
|
+
800 : '#2E7D32',
|
|
122
|
+
900 : '#1B5E20',
|
|
123
|
+
},
|
|
124
|
+
"grey": {
|
|
125
|
+
50 : '#FAFAFA',
|
|
126
|
+
100 : '#F5F5F5',
|
|
127
|
+
200 : '#EEEEEE',
|
|
128
|
+
300 : '#E0E0E0',
|
|
129
|
+
400 : '#BDBDBD',
|
|
130
|
+
500 : '#9E9E9E',
|
|
131
|
+
600 : '#757575',
|
|
132
|
+
700 : '#616161',
|
|
133
|
+
800 : '#424242',
|
|
134
|
+
900 : '#212121',
|
|
135
|
+
},
|
|
136
|
+
"indigo": {
|
|
137
|
+
50 : '#E8EAF6',
|
|
138
|
+
100 : '#C5CAE9',
|
|
139
|
+
200 : '#9FA8DA',
|
|
140
|
+
300 : '#7986CB',
|
|
141
|
+
400 : '#5C6BC0',
|
|
142
|
+
500 : '#3F51B5',
|
|
143
|
+
600 : '#3949AB',
|
|
144
|
+
700 : '#303F9F',
|
|
145
|
+
800 : '#283593',
|
|
146
|
+
900 : '#1A237E',
|
|
147
|
+
},
|
|
148
|
+
"light_blue": {
|
|
149
|
+
50 : '#E1F5FE',
|
|
150
|
+
100 : '#B3E5FC',
|
|
151
|
+
200 : '#81D4FA',
|
|
152
|
+
300 : '#4FC3F7',
|
|
153
|
+
400 : '#29B6F6',
|
|
154
|
+
500 : '#03A9F4',
|
|
155
|
+
600 : '#039BE5',
|
|
156
|
+
700 : '#0288D1',
|
|
157
|
+
800 : '#0277BD',
|
|
158
|
+
900 : '#01579B',
|
|
159
|
+
},
|
|
160
|
+
"light_green": {
|
|
161
|
+
50 : '#F1F8E9',
|
|
162
|
+
100 : '#DCEDC8',
|
|
163
|
+
200 : '#C5E1A5',
|
|
164
|
+
300 : '#AED581',
|
|
165
|
+
400 : '#9CCC65',
|
|
166
|
+
500 : '#8BC34A',
|
|
167
|
+
600 : '#7CB342',
|
|
168
|
+
700 : '#689F38',
|
|
169
|
+
800 : '#558B2F',
|
|
170
|
+
900 : '#33691E',
|
|
171
|
+
},
|
|
172
|
+
"lime": {
|
|
173
|
+
50 : '#F9FBE7',
|
|
174
|
+
100 : '#F0F4C3',
|
|
175
|
+
200 : '#E6EE9C',
|
|
176
|
+
300 : '#DCE775',
|
|
177
|
+
400 : '#D4E157',
|
|
178
|
+
500 : '#CDDC39',
|
|
179
|
+
600 : '#C0CA33',
|
|
180
|
+
700 : '#AFB42B',
|
|
181
|
+
800 : '#9E9D24',
|
|
182
|
+
900 : '#827717',
|
|
183
|
+
},
|
|
184
|
+
"orange": {
|
|
185
|
+
50 : '#FFF3E0',
|
|
186
|
+
100 : '#FFE0B2',
|
|
187
|
+
200 : '#FFCC80',
|
|
188
|
+
300 : '#FFB74D',
|
|
189
|
+
400 : '#FFA726',
|
|
190
|
+
500 : '#FF9800',
|
|
191
|
+
600 : '#FB8C00',
|
|
192
|
+
700 : '#F57C00',
|
|
193
|
+
800 : '#EF6C00',
|
|
194
|
+
900 : '#E65100',
|
|
195
|
+
},
|
|
196
|
+
"pink": {
|
|
197
|
+
50 : '#FCE4EC',
|
|
198
|
+
100 : '#F8BBD0',
|
|
199
|
+
200 : '#F48FB1',
|
|
200
|
+
300 : '#F06292',
|
|
201
|
+
400 : '#EC407A',
|
|
202
|
+
500 : '#E91E63',
|
|
203
|
+
600 : '#D81B60',
|
|
204
|
+
700 : '#C2185B',
|
|
205
|
+
800 : '#AD1457',
|
|
206
|
+
900 : '#880E4F',
|
|
207
|
+
},
|
|
208
|
+
"purple": {
|
|
209
|
+
50 : '#F3E5F5',
|
|
210
|
+
100 : '#E1BEE7',
|
|
211
|
+
200 : '#CE93D8',
|
|
212
|
+
300 : '#BA68C8',
|
|
213
|
+
400 : '#AB47BC',
|
|
214
|
+
500 : '#9C27B0',
|
|
215
|
+
600 : '#8E24AA',
|
|
216
|
+
700 : '#7B1FA2',
|
|
217
|
+
800 : '#6A1B9A',
|
|
218
|
+
900 : '#4A148C',
|
|
219
|
+
},
|
|
220
|
+
"red": {
|
|
221
|
+
50 : '#FFEBEE',
|
|
222
|
+
100 : '#FFCDD2',
|
|
223
|
+
200 : '#EF9A9A',
|
|
224
|
+
300 : '#E57373',
|
|
225
|
+
500 : '#F44336',
|
|
226
|
+
600 : '#E53935',
|
|
227
|
+
700 : '#D32F2F',
|
|
228
|
+
800 : '#C62828',
|
|
229
|
+
900 : '#B71C1C',
|
|
230
|
+
},
|
|
231
|
+
"teal": {
|
|
232
|
+
50 : '#E0F2F1',
|
|
233
|
+
100 : '#B2DFDB',
|
|
234
|
+
200 : '#80CBC4',
|
|
235
|
+
300 : '#4DB6AC',
|
|
236
|
+
400 : '#26A69A',
|
|
237
|
+
500 : '#009688',
|
|
238
|
+
600 : '#00897B',
|
|
239
|
+
700 : '#00796B',
|
|
240
|
+
800 : '#00695C',
|
|
241
|
+
900 : '#004D40',
|
|
242
|
+
},
|
|
243
|
+
"yellow": {
|
|
244
|
+
50 : '#FFFDE7',
|
|
245
|
+
100 : '#FFF9C4',
|
|
246
|
+
200 : '#FFF59D',
|
|
247
|
+
300 : '#FFF176',
|
|
248
|
+
400 : '#FFEE58',
|
|
249
|
+
500 : '#FFEB3B',
|
|
250
|
+
600 : '#FDD835',
|
|
251
|
+
700 : '#FBC02D',
|
|
252
|
+
800 : '#F9A825',
|
|
253
|
+
900 : '#F57F17',
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
def fix_data_file(datafile, nelements):
|
|
258
|
+
"""
|
|
259
|
+
Change the atom types keyword in the structure file
|
|
260
|
+
"""
|
|
261
|
+
lines = []
|
|
262
|
+
with open(datafile, 'r') as fin:
|
|
263
|
+
for line in fin:
|
|
264
|
+
if 'atom types' in line:
|
|
265
|
+
lines.append(f'{nelements} atom types\n')
|
|
266
|
+
else:
|
|
267
|
+
lines.append(line)
|
|
268
|
+
outfile = datafile + 'mod.data'
|
|
269
|
+
with open(outfile, 'w') as fout:
|
|
270
|
+
for line in lines:
|
|
271
|
+
fout.write(line)
|
|
272
|
+
return outfile
|
|
273
|
+
|
|
274
|
+
class CScale:
|
|
275
|
+
def __init__(self):
|
|
276
|
+
self._input_chemical_composition = None
|
|
277
|
+
self._output_chemical_composition = None
|
|
278
|
+
self.restrictions = []
|
|
279
|
+
|
|
280
|
+
@property
|
|
281
|
+
def input_chemical_composition(self):
|
|
282
|
+
return self._input_chemical_composition
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def output_chemical_composition(self):
|
|
286
|
+
return self._output_chemical_composition
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class SimpleCalculation:
|
|
290
|
+
"""
|
|
291
|
+
Simple calc class
|
|
292
|
+
"""
|
|
293
|
+
def __init__(self, lattice,
|
|
294
|
+
element,
|
|
295
|
+
input_chemical_composition,
|
|
296
|
+
output_chemical_composition):
|
|
297
|
+
self.lattice = lattice
|
|
298
|
+
self.element = element
|
|
299
|
+
self.composition_scaling = CScale()
|
|
300
|
+
self.composition_scaling._input_chemical_composition = input_chemical_composition
|
|
301
|
+
self.composition_scaling._output_chemical_composition = output_chemical_composition
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def prepare_inputs_for_phase_diagram(inputyamlfile, calculation_base_name=None):
|
|
305
|
+
with open(inputyamlfile, 'r') as fin:
|
|
306
|
+
data = yaml.safe_load(fin)
|
|
307
|
+
|
|
308
|
+
if calculation_base_name is None:
|
|
309
|
+
calculation_base_name = inputyamlfile
|
|
310
|
+
|
|
311
|
+
for phase in data['phases']:
|
|
312
|
+
phase_reference_state = phase['reference_phase']
|
|
313
|
+
phase_name = phase['phase_name']
|
|
314
|
+
|
|
315
|
+
comps = phase['composition']
|
|
316
|
+
reference_element = comps["reference_element"]
|
|
317
|
+
if "use_composition_scaling" in comps.keys():
|
|
318
|
+
use_composition_scaling = bool(comps["use_composition_scaling"])
|
|
319
|
+
else:
|
|
320
|
+
use_composition_scaling = True
|
|
321
|
+
if str(phase_reference_state) == 'liquid':
|
|
322
|
+
use_composition_scaling = False
|
|
323
|
+
|
|
324
|
+
other_element_list = copy.deepcopy(phase['element'])
|
|
325
|
+
other_element_list.remove(reference_element)
|
|
326
|
+
other_element = other_element_list[0]
|
|
327
|
+
|
|
328
|
+
#convert to list if scalar
|
|
329
|
+
if not isinstance(comps['range'], list):
|
|
330
|
+
comps["range"] = [comps["range"]]
|
|
331
|
+
if len(comps["range"]) == 2:
|
|
332
|
+
comp_arr = np.arange(comps['range'][0], comps['range'][-1], comps['interval'])
|
|
333
|
+
last_val = comps['range'][-1]
|
|
334
|
+
if last_val not in comp_arr:
|
|
335
|
+
comp_arr = np.append(comp_arr, last_val)
|
|
336
|
+
ncomps = len(comp_arr)
|
|
337
|
+
is_reference = np.abs(comp_arr-comps['reference']) < 1E-5
|
|
338
|
+
elif len(comps["range"]) == 1:
|
|
339
|
+
ncomps = 1
|
|
340
|
+
comp_arr = [comps["range"][0]]
|
|
341
|
+
is_reference = [True]
|
|
342
|
+
else:
|
|
343
|
+
raise ValueError("Composition range should be scalar of list of two values!")
|
|
344
|
+
|
|
345
|
+
temps = phase["temperature"]
|
|
346
|
+
if not isinstance(temps['range'], list):
|
|
347
|
+
temps["range"] = [temps["range"]]
|
|
348
|
+
if len(temps["range"]) == 2:
|
|
349
|
+
ntemps = int((temps['range'][-1]-temps['range'][0])/temps['interval'])+1
|
|
350
|
+
temp_arr = np.linspace(temps['range'][0], temps['range'][-1], ntemps, endpoint=True)
|
|
351
|
+
elif len(temps["range"]) == 1:
|
|
352
|
+
ntemps = 1
|
|
353
|
+
temp_arr = [temps["range"][0]]
|
|
354
|
+
else:
|
|
355
|
+
raise ValueError("Temperature range should be scalar of list of two values!")
|
|
356
|
+
|
|
357
|
+
all_calculations = []
|
|
358
|
+
|
|
359
|
+
for count, comp in enumerate(comp_arr):
|
|
360
|
+
#check if ref comp equals given comp
|
|
361
|
+
if is_reference[count]:
|
|
362
|
+
#copy the dict
|
|
363
|
+
calc = copy.deepcopy(phase)
|
|
364
|
+
|
|
365
|
+
#pop extra keys which are not needed
|
|
366
|
+
#we dont kick out phase_name
|
|
367
|
+
extra_keys = ['composition', 'monte_carlo']
|
|
368
|
+
for key in extra_keys:
|
|
369
|
+
_ = calc.pop(key, None)
|
|
370
|
+
|
|
371
|
+
#update file if needed
|
|
372
|
+
outfile = fix_data_file(calc['lattice'], len(calc['element']))
|
|
373
|
+
|
|
374
|
+
#add ref phase, needed
|
|
375
|
+
calc['reference_phase'] = str(phase_reference_state)
|
|
376
|
+
calc['reference_composition'] = comps['reference']
|
|
377
|
+
calc['mode'] = str('fe')
|
|
378
|
+
calc['folder_prefix'] = f'{phase_name}-{comp:.2f}'
|
|
379
|
+
calc['lattice'] = str(outfile)
|
|
380
|
+
|
|
381
|
+
#now we need to run this for different temp
|
|
382
|
+
for temp in temp_arr:
|
|
383
|
+
calc_for_temp = copy.deepcopy(calc)
|
|
384
|
+
calc_for_temp['temperature'] = int(temp)
|
|
385
|
+
all_calculations.append(calc_for_temp)
|
|
386
|
+
else:
|
|
387
|
+
#off stoichiometric
|
|
388
|
+
#copy the dict
|
|
389
|
+
calc = copy.deepcopy(phase)
|
|
390
|
+
|
|
391
|
+
#first thing first, we need to calculate the number of atoms
|
|
392
|
+
#we follow the convention that composition is always given with the second species
|
|
393
|
+
n_atoms = np.sum(calc['composition']['number_of_atoms'])
|
|
394
|
+
|
|
395
|
+
#find number of atoms of second species
|
|
396
|
+
output_chemical_composition = {}
|
|
397
|
+
n_species_b = int(np.round(comp*n_atoms, decimals=0))
|
|
398
|
+
output_chemical_composition[reference_element] = n_species_b
|
|
399
|
+
|
|
400
|
+
n_species_a = int(n_atoms-n_species_b)
|
|
401
|
+
output_chemical_composition[other_element] = n_species_a
|
|
402
|
+
|
|
403
|
+
if n_species_a == 0:
|
|
404
|
+
raise ValueError("Please add pure phase as a new entry!")
|
|
405
|
+
#create input comp dict and output comp dict
|
|
406
|
+
input_chemical_composition = {element:number for element, number in zip(calc['element'],
|
|
407
|
+
calc['composition']['number_of_atoms'])}
|
|
408
|
+
|
|
409
|
+
#good, now we need to write such a structure out; likely better to use working directory for that
|
|
410
|
+
folder_prefix = f'{phase_name}-{comp:.2f}'
|
|
411
|
+
calc['reference_composition'] = comps['reference']
|
|
412
|
+
#if solid, its very easy; kinda
|
|
413
|
+
#if calc['reference_phase'] == 'solid':
|
|
414
|
+
if use_composition_scaling:
|
|
415
|
+
#this is solid , and comp scale is turned on
|
|
416
|
+
#pop extra keys which are not needed
|
|
417
|
+
#we dont kick out phase_name
|
|
418
|
+
extra_keys = ['composition', 'reference_phase']
|
|
419
|
+
for key in extra_keys:
|
|
420
|
+
_ = calc.pop(key, None)
|
|
421
|
+
|
|
422
|
+
#just submit comp scales
|
|
423
|
+
#add ref phase, needed
|
|
424
|
+
calc['mode'] = str('composition_scaling')
|
|
425
|
+
calc['folder_prefix'] = folder_prefix
|
|
426
|
+
calc['composition_scaling'] = {}
|
|
427
|
+
calc['composition_scaling']['output_chemical_composition'] = output_chemical_composition
|
|
428
|
+
|
|
429
|
+
else:
|
|
430
|
+
#manually create a mixed structure - not that the pair style is always ok :)
|
|
431
|
+
|
|
432
|
+
outfile = os.path.join(os.getcwd(), os.path.basename(calc['lattice'])+folder_prefix+'.comp.mod')
|
|
433
|
+
#print(f'finding comp trf from {input_chemical_composition} to {output_chemical_composition}')
|
|
434
|
+
#write_structure(calc['lattice'], input_chemical_composition, output_chemical_composition, outfile)
|
|
435
|
+
|
|
436
|
+
simplecalc = SimpleCalculation(calc['lattice'],
|
|
437
|
+
calc["element"],
|
|
438
|
+
input_chemical_composition,
|
|
439
|
+
output_chemical_composition)
|
|
440
|
+
compsc = CompositionTransformation(simplecalc)
|
|
441
|
+
compsc.write_structure(outfile)
|
|
442
|
+
|
|
443
|
+
#pop extra keys which are not needed
|
|
444
|
+
#we dont kick out phase name
|
|
445
|
+
extra_keys = ['composition']
|
|
446
|
+
for key in extra_keys:
|
|
447
|
+
_ = calc.pop(key, None)
|
|
448
|
+
|
|
449
|
+
#add ref phase, needed
|
|
450
|
+
calc['mode'] = str('fe')
|
|
451
|
+
calc['folder_prefix'] = folder_prefix
|
|
452
|
+
calc['lattice'] = str(outfile)
|
|
453
|
+
|
|
454
|
+
#now we need to run this for different temp
|
|
455
|
+
for temp in temp_arr:
|
|
456
|
+
calc_for_temp = copy.deepcopy(calc)
|
|
457
|
+
calc_for_temp['temperature'] = int(temp)
|
|
458
|
+
all_calculations.append(calc_for_temp)
|
|
459
|
+
|
|
460
|
+
#finish and write up the file
|
|
461
|
+
output_data = {"calculations": all_calculations}
|
|
462
|
+
for rep in ['.yml', '.yaml']:
|
|
463
|
+
calculation_base_name = calculation_base_name.replace(rep, '')
|
|
464
|
+
|
|
465
|
+
outfile_phase = phase_name + '_' + calculation_base_name + ".yaml"
|
|
466
|
+
with open(outfile_phase, 'w') as fout:
|
|
467
|
+
yaml.safe_dump(output_data, fout)
|
|
468
|
+
print(f'Total {len(all_calculations)} calculations found for phase {phase_name}, written to {outfile_phase}')
|
|
469
|
+
|
|
19
470
|
|
|
20
471
|
def _get_temp_arg(tarr, temp, threshold=1E-1):
|
|
21
472
|
if tarr is None:
|
|
@@ -91,7 +542,9 @@ def get_phase_free_energy(df, phase, temp,
|
|
|
91
542
|
composition_grid=10000,
|
|
92
543
|
composition_cutoff=None,
|
|
93
544
|
reset_value=1,
|
|
94
|
-
plot=False
|
|
545
|
+
plot=False,
|
|
546
|
+
end_weight=3,
|
|
547
|
+
end_indices=4):
|
|
95
548
|
"""
|
|
96
549
|
Get the free energy of a phase as a function of composition.
|
|
97
550
|
|
|
@@ -165,7 +618,9 @@ def get_phase_free_energy(df, phase, temp,
|
|
|
165
618
|
else:
|
|
166
619
|
entropy_term = []
|
|
167
620
|
|
|
168
|
-
fe_fit = _get_free_energy_fit(composition, fes, fit_order=fit_order
|
|
621
|
+
fe_fit = _get_free_energy_fit(composition, fes, fit_order=fit_order,
|
|
622
|
+
end_weight=end_weight,
|
|
623
|
+
end_indices=end_indices)
|
|
169
624
|
compfine = np.linspace(np.min(composition), np.max(composition), composition_grid)
|
|
170
625
|
|
|
171
626
|
#now fit on the comp grid again
|
|
@@ -234,21 +689,25 @@ def get_free_energy_mixing(dict_list, threshold=1E-3):
|
|
|
234
689
|
d["free_energy_mix"] = ref
|
|
235
690
|
return dict_list
|
|
236
691
|
|
|
237
|
-
def create_color_list(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
combinations = list(itertools.combinations_with_replacement(phase_list, 2))
|
|
243
|
-
|
|
244
|
-
if len(combinations) > len(color_list):
|
|
245
|
-
raise ValueError(f'need {len(combinations)} colors, please provide using color_list=')
|
|
246
|
-
|
|
692
|
+
def create_color_list(phases):
|
|
693
|
+
combinations_list = ['-'.join(pair) for pair in combinations(phases, 2)]
|
|
694
|
+
same_element_pairs = ['-'.join([item, item]) for item in phases]
|
|
695
|
+
final_combinations = same_element_pairs + combinations_list
|
|
696
|
+
|
|
247
697
|
color_dict = {}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
698
|
+
|
|
699
|
+
color_keys = list(matcolors.keys())
|
|
700
|
+
int_keys = list(matcolors['red'].keys())
|
|
701
|
+
|
|
702
|
+
for count, combination in enumerate(final_combinations):
|
|
703
|
+
index = count%len(color_keys)
|
|
704
|
+
second_index = -1
|
|
705
|
+
color_hex = matcolors[color_keys[int(index)]][int_keys[second_index]]
|
|
706
|
+
color_dict[combination] = color_hex
|
|
707
|
+
raw = combination.split('-')
|
|
708
|
+
if raw[0] != raw[1]:
|
|
709
|
+
reversecombo = f'{raw[1]}-{raw[0]}'
|
|
710
|
+
color_dict[reversecombo] = color_hex
|
|
252
711
|
return color_dict
|
|
253
712
|
|
|
254
713
|
def get_tangent_type(dict_list, tangent, energy):
|
|
@@ -298,16 +757,15 @@ def get_tangent_type(dict_list, tangent, energy):
|
|
|
298
757
|
def get_common_tangents(dict_list,
|
|
299
758
|
peak_cutoff=0.01,
|
|
300
759
|
plot=False,
|
|
301
|
-
remove_self_tangents_for=[]
|
|
302
|
-
color_dict=None):
|
|
760
|
+
remove_self_tangents_for=[]):
|
|
303
761
|
"""
|
|
304
762
|
Get common tangent constructions using convex hull method
|
|
305
763
|
"""
|
|
306
764
|
points = np.vstack([np.column_stack((d["composition"],
|
|
307
765
|
d["free_energy_mix"])) for d in dict_list])
|
|
308
766
|
|
|
309
|
-
if color_dict is None:
|
|
310
|
-
|
|
767
|
+
#if color_dict is None:
|
|
768
|
+
# color_dict = create_color_list(dict_list)
|
|
311
769
|
|
|
312
770
|
#make common tangent constructions
|
|
313
771
|
#term checks if two different phases are stable at the end points, then common tangent is needed
|
|
@@ -328,7 +786,7 @@ def get_common_tangents(dict_list,
|
|
|
328
786
|
|
|
329
787
|
tangents = []
|
|
330
788
|
energies = []
|
|
331
|
-
|
|
789
|
+
tangent_types = []
|
|
332
790
|
phases = []
|
|
333
791
|
|
|
334
792
|
for d in dist:
|
|
@@ -336,10 +794,16 @@ def get_common_tangents(dict_list,
|
|
|
336
794
|
e = [convex_points[sargs][d], convex_points[sargs][d+1]]
|
|
337
795
|
phase_str = get_tangent_type(dict_list, t, e)
|
|
338
796
|
|
|
339
|
-
|
|
797
|
+
remove = False
|
|
798
|
+
ps = phase_str.split('-')
|
|
799
|
+
if ps[0] == ps[1]:
|
|
800
|
+
if ps[0] in remove_self_tangents_for:
|
|
801
|
+
remove = True
|
|
802
|
+
|
|
803
|
+
if not remove:
|
|
340
804
|
tangents.append(t)
|
|
341
805
|
energies.append(e)
|
|
342
|
-
|
|
806
|
+
tangent_types.append(phase_str)
|
|
343
807
|
phases.append(phase_str.split("-"))
|
|
344
808
|
|
|
345
809
|
if plot:
|
|
@@ -349,15 +813,27 @@ def get_common_tangents(dict_list,
|
|
|
349
813
|
plt.plot(t, e, color="black", ls="dashed")
|
|
350
814
|
plt.ylim(top=0.0)
|
|
351
815
|
|
|
352
|
-
return np.array(tangents), np.array(energies), np.array(
|
|
816
|
+
return np.array(tangents), np.array(energies), np.array(tangent_types), np.array(phases)
|
|
353
817
|
|
|
354
818
|
|
|
355
819
|
def plot_phase_diagram(tangents, temperature,
|
|
356
|
-
|
|
820
|
+
tangent_types,
|
|
821
|
+
phases,
|
|
357
822
|
edgecolor="#37474f",
|
|
358
823
|
linewidth=1,
|
|
359
824
|
linestyle='-'):
|
|
360
825
|
|
|
826
|
+
#get a phase list
|
|
827
|
+
color_dict = create_color_list(phases)
|
|
828
|
+
minimal_color_dict = {}
|
|
829
|
+
color_list = []
|
|
830
|
+
for key, val in color_dict.items():
|
|
831
|
+
if val not in color_list:
|
|
832
|
+
color_list.append(val)
|
|
833
|
+
minimal_color_dict[key] = val
|
|
834
|
+
|
|
835
|
+
legend_patches = [mpatches.Patch(color=color, label=label) for label, color in minimal_color_dict.items()]
|
|
836
|
+
|
|
361
837
|
fig, ax = plt.subplots(edgecolor=edgecolor)
|
|
362
838
|
|
|
363
839
|
for count, x in enumerate(tangents):
|
|
@@ -366,7 +842,8 @@ def plot_phase_diagram(tangents, temperature,
|
|
|
366
842
|
[temperature[count], temperature[count]],
|
|
367
843
|
linestyle,
|
|
368
844
|
lw=linewidth,
|
|
369
|
-
c=
|
|
845
|
+
c=color_dict[tangent_types[count][c]],
|
|
370
846
|
)
|
|
847
|
+
ax.legend(handles=legend_patches, loc='center left', bbox_to_anchor=(1, 0.5))
|
|
371
848
|
return fig
|
|
372
849
|
|