calphy 1.3.11__py3-none-any.whl → 1.4.2__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 +815 -378
- calphy/phase_diagram.py +500 -26
- calphy/postprocessing.py +274 -5
- calphy/routines.py +13 -2
- calphy/scheduler.py +2 -0
- calphy/solid.py +35 -0
- {calphy-1.3.11.dist-info → calphy-1.4.2.dist-info}/METADATA +14 -2
- calphy-1.4.2.dist-info/RECORD +25 -0
- {calphy-1.3.11.dist-info → calphy-1.4.2.dist-info}/WHEEL +1 -1
- {calphy-1.3.11.dist-info → calphy-1.4.2.dist-info}/entry_points.txt +1 -0
- calphy-1.3.11.dist-info/RECORD +0 -25
- {calphy-1.3.11.dist-info → calphy-1.4.2.dist-info/licenses}/LICENSE +0 -0
- {calphy-1.3.11.dist-info → calphy-1.4.2.dist-info}/top_level.txt +0 -0
calphy/phase_diagram.py
CHANGED
|
@@ -4,18 +4,466 @@ 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
|
+
use_composition_scaling = bool(comps["use_composition_scaling"])
|
|
318
|
+
if str(phase_reference_state) == 'liquid':
|
|
319
|
+
use_composition_scaling = False
|
|
320
|
+
|
|
321
|
+
other_element_list = copy.deepcopy(phase['element'])
|
|
322
|
+
other_element_list.remove(reference_element)
|
|
323
|
+
other_element = other_element_list[0]
|
|
324
|
+
|
|
325
|
+
#convert to list if scalar
|
|
326
|
+
if not isinstance(comps['range'], list):
|
|
327
|
+
comps["range"] = [comps["range"]]
|
|
328
|
+
if len(comps["range"]) == 2:
|
|
329
|
+
comp_arr = np.arange(comps['range'][0], comps['range'][-1], comps['interval'])
|
|
330
|
+
last_val = comps['range'][-1]
|
|
331
|
+
if last_val not in comp_arr:
|
|
332
|
+
comp_arr = np.append(comp_arr, last_val)
|
|
333
|
+
ncomps = len(comp_arr)
|
|
334
|
+
is_reference = np.abs(comp_arr-comps['reference']) < 1E-5
|
|
335
|
+
elif len(comps["range"]) == 1:
|
|
336
|
+
ncomps = 1
|
|
337
|
+
comp_arr = [comps["range"][0]]
|
|
338
|
+
is_reference = [True]
|
|
339
|
+
else:
|
|
340
|
+
raise ValueError("Composition range should be scalar of list of two values!")
|
|
341
|
+
|
|
342
|
+
temps = phase["temperature"]
|
|
343
|
+
if not isinstance(temps['range'], list):
|
|
344
|
+
temps["range"] = [temps["range"]]
|
|
345
|
+
if len(temps["range"]) == 2:
|
|
346
|
+
ntemps = int((temps['range'][-1]-temps['range'][0])/temps['interval'])+1
|
|
347
|
+
temp_arr = np.linspace(temps['range'][0], temps['range'][-1], ntemps, endpoint=True)
|
|
348
|
+
elif len(temps["range"]) == 1:
|
|
349
|
+
ntemps = 1
|
|
350
|
+
temp_arr = [temps["range"][0]]
|
|
351
|
+
else:
|
|
352
|
+
raise ValueError("Temperature range should be scalar of list of two values!")
|
|
353
|
+
|
|
354
|
+
all_calculations = []
|
|
355
|
+
|
|
356
|
+
for count, comp in enumerate(comp_arr):
|
|
357
|
+
#check if ref comp equals given comp
|
|
358
|
+
if is_reference[count]:
|
|
359
|
+
#copy the dict
|
|
360
|
+
calc = copy.deepcopy(phase)
|
|
361
|
+
|
|
362
|
+
#pop extra keys which are not needed
|
|
363
|
+
#we dont kick out phase_name
|
|
364
|
+
extra_keys = ['composition', 'monte_carlo']
|
|
365
|
+
for key in extra_keys:
|
|
366
|
+
_ = calc.pop(key, None)
|
|
367
|
+
|
|
368
|
+
#update file if needed
|
|
369
|
+
outfile = fix_data_file(calc['lattice'], len(calc['element']))
|
|
370
|
+
|
|
371
|
+
#add ref phase, needed
|
|
372
|
+
calc['reference_phase'] = str(phase_reference_state)
|
|
373
|
+
calc['reference_composition'] = comps['reference']
|
|
374
|
+
calc['mode'] = str('fe')
|
|
375
|
+
calc['folder_prefix'] = f'{phase_name}-{comp:.2f}'
|
|
376
|
+
calc['lattice'] = str(outfile)
|
|
377
|
+
|
|
378
|
+
#now we need to run this for different temp
|
|
379
|
+
for temp in temp_arr:
|
|
380
|
+
calc_for_temp = copy.deepcopy(calc)
|
|
381
|
+
calc_for_temp['temperature'] = int(temp)
|
|
382
|
+
all_calculations.append(calc_for_temp)
|
|
383
|
+
else:
|
|
384
|
+
#off stoichiometric
|
|
385
|
+
#copy the dict
|
|
386
|
+
calc = copy.deepcopy(phase)
|
|
387
|
+
|
|
388
|
+
#first thing first, we need to calculate the number of atoms
|
|
389
|
+
#we follow the convention that composition is always given with the second species
|
|
390
|
+
n_atoms = np.sum(calc['composition']['number_of_atoms'])
|
|
391
|
+
|
|
392
|
+
#find number of atoms of second species
|
|
393
|
+
output_chemical_composition = {}
|
|
394
|
+
n_species_b = int(np.round(comp*n_atoms, decimals=0))
|
|
395
|
+
output_chemical_composition[reference_element] = n_species_b
|
|
396
|
+
|
|
397
|
+
n_species_a = int(n_atoms-n_species_b)
|
|
398
|
+
output_chemical_composition[other_element] = n_species_a
|
|
399
|
+
|
|
400
|
+
if n_species_a == 0:
|
|
401
|
+
raise ValueError("Please add pure phase as a new entry!")
|
|
402
|
+
#create input comp dict and output comp dict
|
|
403
|
+
input_chemical_composition = {element:number for element, number in zip(calc['element'],
|
|
404
|
+
calc['composition']['number_of_atoms'])}
|
|
405
|
+
|
|
406
|
+
#good, now we need to write such a structure out; likely better to use working directory for that
|
|
407
|
+
folder_prefix = f'{phase_name}-{comp:.2f}'
|
|
408
|
+
calc['reference_composition'] = comps['reference']
|
|
409
|
+
#if solid, its very easy; kinda
|
|
410
|
+
#if calc['reference_phase'] == 'solid':
|
|
411
|
+
if use_composition_scaling:
|
|
412
|
+
#this is solid , and comp scale is turned on
|
|
413
|
+
#pop extra keys which are not needed
|
|
414
|
+
#we dont kick out phase_name
|
|
415
|
+
extra_keys = ['composition', 'reference_phase']
|
|
416
|
+
for key in extra_keys:
|
|
417
|
+
_ = calc.pop(key, None)
|
|
418
|
+
|
|
419
|
+
#just submit comp scales
|
|
420
|
+
#add ref phase, needed
|
|
421
|
+
calc['mode'] = str('composition_scaling')
|
|
422
|
+
calc['folder_prefix'] = folder_prefix
|
|
423
|
+
calc['composition_scaling'] = {}
|
|
424
|
+
calc['composition_scaling']['output_chemical_composition'] = output_chemical_composition
|
|
425
|
+
|
|
426
|
+
else:
|
|
427
|
+
#manually create a mixed structure - not that the pair style is always ok :)
|
|
428
|
+
|
|
429
|
+
outfile = os.path.join(os.getcwd(), os.path.basename(calc['lattice'])+folder_prefix+'.comp.mod')
|
|
430
|
+
#print(f'finding comp trf from {input_chemical_composition} to {output_chemical_composition}')
|
|
431
|
+
#write_structure(calc['lattice'], input_chemical_composition, output_chemical_composition, outfile)
|
|
432
|
+
|
|
433
|
+
simplecalc = SimpleCalculation(calc['lattice'],
|
|
434
|
+
calc["element"],
|
|
435
|
+
input_chemical_composition,
|
|
436
|
+
output_chemical_composition)
|
|
437
|
+
compsc = CompositionTransformation(simplecalc)
|
|
438
|
+
compsc.write_structure(outfile)
|
|
439
|
+
|
|
440
|
+
#pop extra keys which are not needed
|
|
441
|
+
#we dont kick out phase name
|
|
442
|
+
extra_keys = ['composition']
|
|
443
|
+
for key in extra_keys:
|
|
444
|
+
_ = calc.pop(key, None)
|
|
445
|
+
|
|
446
|
+
#add ref phase, needed
|
|
447
|
+
calc['mode'] = str('fe')
|
|
448
|
+
calc['folder_prefix'] = folder_prefix
|
|
449
|
+
calc['lattice'] = str(outfile)
|
|
450
|
+
|
|
451
|
+
#now we need to run this for different temp
|
|
452
|
+
for temp in temp_arr:
|
|
453
|
+
calc_for_temp = copy.deepcopy(calc)
|
|
454
|
+
calc_for_temp['temperature'] = int(temp)
|
|
455
|
+
all_calculations.append(calc_for_temp)
|
|
456
|
+
|
|
457
|
+
#finish and write up the file
|
|
458
|
+
output_data = {"calculations": all_calculations}
|
|
459
|
+
for rep in ['.yml', '.yaml']:
|
|
460
|
+
calculation_base_name = calculation_base_name.replace(rep, '')
|
|
461
|
+
|
|
462
|
+
outfile_phase = phase_name + '_' + calculation_base_name + ".yaml"
|
|
463
|
+
with open(outfile_phase, 'w') as fout:
|
|
464
|
+
yaml.safe_dump(output_data, fout)
|
|
465
|
+
print(f'Total {len(all_calculations)} calculations found for phase {phase_name}, written to {outfile_phase}')
|
|
466
|
+
|
|
19
467
|
|
|
20
468
|
def _get_temp_arg(tarr, temp, threshold=1E-1):
|
|
21
469
|
if tarr is None:
|
|
@@ -91,7 +539,9 @@ def get_phase_free_energy(df, phase, temp,
|
|
|
91
539
|
composition_grid=10000,
|
|
92
540
|
composition_cutoff=None,
|
|
93
541
|
reset_value=1,
|
|
94
|
-
plot=False
|
|
542
|
+
plot=False,
|
|
543
|
+
end_weight=3,
|
|
544
|
+
end_indices=4):
|
|
95
545
|
"""
|
|
96
546
|
Get the free energy of a phase as a function of composition.
|
|
97
547
|
|
|
@@ -165,7 +615,9 @@ def get_phase_free_energy(df, phase, temp,
|
|
|
165
615
|
else:
|
|
166
616
|
entropy_term = []
|
|
167
617
|
|
|
168
|
-
fe_fit = _get_free_energy_fit(composition, fes, fit_order=fit_order
|
|
618
|
+
fe_fit = _get_free_energy_fit(composition, fes, fit_order=fit_order,
|
|
619
|
+
end_weight=end_weight,
|
|
620
|
+
end_indices=end_indices)
|
|
169
621
|
compfine = np.linspace(np.min(composition), np.max(composition), composition_grid)
|
|
170
622
|
|
|
171
623
|
#now fit on the comp grid again
|
|
@@ -234,21 +686,25 @@ def get_free_energy_mixing(dict_list, threshold=1E-3):
|
|
|
234
686
|
d["free_energy_mix"] = ref
|
|
235
687
|
return dict_list
|
|
236
688
|
|
|
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
|
-
|
|
689
|
+
def create_color_list(phases):
|
|
690
|
+
combinations_list = ['-'.join(pair) for pair in combinations(phases, 2)]
|
|
691
|
+
same_element_pairs = ['-'.join([item, item]) for item in phases]
|
|
692
|
+
final_combinations = same_element_pairs + combinations_list
|
|
693
|
+
|
|
247
694
|
color_dict = {}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
695
|
+
|
|
696
|
+
color_keys = list(matcolors.keys())
|
|
697
|
+
int_keys = list(matcolors['red'].keys())
|
|
698
|
+
|
|
699
|
+
for count, combination in enumerate(final_combinations):
|
|
700
|
+
index = count%len(color_keys)
|
|
701
|
+
second_index = -1
|
|
702
|
+
color_hex = matcolors[color_keys[int(index)]][int_keys[second_index]]
|
|
703
|
+
color_dict[combination] = color_hex
|
|
704
|
+
raw = combination.split('-')
|
|
705
|
+
if raw[0] != raw[1]:
|
|
706
|
+
reversecombo = f'{raw[1]}-{raw[0]}'
|
|
707
|
+
color_dict[reversecombo] = color_hex
|
|
252
708
|
return color_dict
|
|
253
709
|
|
|
254
710
|
def get_tangent_type(dict_list, tangent, energy):
|
|
@@ -298,16 +754,15 @@ def get_tangent_type(dict_list, tangent, energy):
|
|
|
298
754
|
def get_common_tangents(dict_list,
|
|
299
755
|
peak_cutoff=0.01,
|
|
300
756
|
plot=False,
|
|
301
|
-
remove_self_tangents_for=[]
|
|
302
|
-
color_dict=None):
|
|
757
|
+
remove_self_tangents_for=[]):
|
|
303
758
|
"""
|
|
304
759
|
Get common tangent constructions using convex hull method
|
|
305
760
|
"""
|
|
306
761
|
points = np.vstack([np.column_stack((d["composition"],
|
|
307
762
|
d["free_energy_mix"])) for d in dict_list])
|
|
308
763
|
|
|
309
|
-
if color_dict is None:
|
|
310
|
-
|
|
764
|
+
#if color_dict is None:
|
|
765
|
+
# color_dict = create_color_list(dict_list)
|
|
311
766
|
|
|
312
767
|
#make common tangent constructions
|
|
313
768
|
#term checks if two different phases are stable at the end points, then common tangent is needed
|
|
@@ -328,7 +783,7 @@ def get_common_tangents(dict_list,
|
|
|
328
783
|
|
|
329
784
|
tangents = []
|
|
330
785
|
energies = []
|
|
331
|
-
|
|
786
|
+
tangent_types = []
|
|
332
787
|
phases = []
|
|
333
788
|
|
|
334
789
|
for d in dist:
|
|
@@ -336,10 +791,16 @@ def get_common_tangents(dict_list,
|
|
|
336
791
|
e = [convex_points[sargs][d], convex_points[sargs][d+1]]
|
|
337
792
|
phase_str = get_tangent_type(dict_list, t, e)
|
|
338
793
|
|
|
339
|
-
|
|
794
|
+
remove = False
|
|
795
|
+
ps = phase_str.split('-')
|
|
796
|
+
if ps[0] == ps[1]:
|
|
797
|
+
if ps[0] in remove_self_tangents_for:
|
|
798
|
+
remove = True
|
|
799
|
+
|
|
800
|
+
if not remove:
|
|
340
801
|
tangents.append(t)
|
|
341
802
|
energies.append(e)
|
|
342
|
-
|
|
803
|
+
tangent_types.append(phase_str)
|
|
343
804
|
phases.append(phase_str.split("-"))
|
|
344
805
|
|
|
345
806
|
if plot:
|
|
@@ -349,15 +810,27 @@ def get_common_tangents(dict_list,
|
|
|
349
810
|
plt.plot(t, e, color="black", ls="dashed")
|
|
350
811
|
plt.ylim(top=0.0)
|
|
351
812
|
|
|
352
|
-
return np.array(tangents), np.array(energies), np.array(
|
|
813
|
+
return np.array(tangents), np.array(energies), np.array(tangent_types), np.array(phases)
|
|
353
814
|
|
|
354
815
|
|
|
355
816
|
def plot_phase_diagram(tangents, temperature,
|
|
356
|
-
|
|
817
|
+
tangent_types,
|
|
818
|
+
phases,
|
|
357
819
|
edgecolor="#37474f",
|
|
358
820
|
linewidth=1,
|
|
359
821
|
linestyle='-'):
|
|
360
822
|
|
|
823
|
+
#get a phase list
|
|
824
|
+
color_dict = create_color_list(phases)
|
|
825
|
+
minimal_color_dict = {}
|
|
826
|
+
color_list = []
|
|
827
|
+
for key, val in color_dict.items():
|
|
828
|
+
if val not in color_list:
|
|
829
|
+
color_list.append(val)
|
|
830
|
+
minimal_color_dict[key] = val
|
|
831
|
+
|
|
832
|
+
legend_patches = [mpatches.Patch(color=color, label=label) for label, color in minimal_color_dict.items()]
|
|
833
|
+
|
|
361
834
|
fig, ax = plt.subplots(edgecolor=edgecolor)
|
|
362
835
|
|
|
363
836
|
for count, x in enumerate(tangents):
|
|
@@ -366,7 +839,8 @@ def plot_phase_diagram(tangents, temperature,
|
|
|
366
839
|
[temperature[count], temperature[count]],
|
|
367
840
|
linestyle,
|
|
368
841
|
lw=linewidth,
|
|
369
|
-
c=
|
|
842
|
+
c=color_dict[tangent_types[count][c]],
|
|
370
843
|
)
|
|
844
|
+
ax.legend(handles=legend_patches, loc='center left', bbox_to_anchor=(1, 0.5))
|
|
371
845
|
return fig
|
|
372
846
|
|