calphy 1.3.10__tar.gz → 1.3.11__tar.gz
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-1.3.10/calphy.egg-info → calphy-1.3.11}/PKG-INFO +1 -1
- {calphy-1.3.10 → calphy-1.3.11}/calphy/__init__.py +1 -1
- {calphy-1.3.10 → calphy-1.3.11}/calphy/input.py +1 -13
- {calphy-1.3.10 → calphy-1.3.11}/calphy/integrators.py +68 -35
- {calphy-1.3.10 → calphy-1.3.11}/calphy/phase.py +4 -0
- calphy-1.3.11/calphy/postprocessing.py +148 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/solid.py +6 -3
- {calphy-1.3.10 → calphy-1.3.11/calphy.egg-info}/PKG-INFO +1 -1
- {calphy-1.3.10 → calphy-1.3.11}/calphy.egg-info/SOURCES.txt +1 -0
- {calphy-1.3.10 → calphy-1.3.11}/setup.py +1 -1
- {calphy-1.3.10 → calphy-1.3.11}/LICENSE +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/MANIFEST.in +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/README.md +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/alchemy.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/clitools.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/composition_transformation.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/errors.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/helpers.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/kernel.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/liquid.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/phase_diagram.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/queuekernel.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/routines.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/scheduler.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/splines.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy/utils.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy.egg-info/dependency_links.txt +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy.egg-info/entry_points.txt +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy.egg-info/not-zip-safe +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy.egg-info/requires.txt +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/calphy.egg-info/top_level.txt +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/setup.cfg +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/tests/test_helpers.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/tests/test_integrators.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/tests/test_options.py +0 -0
- {calphy-1.3.10 → calphy-1.3.11}/tests/test_solid_methods.py +0 -0
|
@@ -40,19 +40,7 @@ from pyscal3.core import structure_dict, element_dict, _make_crystal
|
|
|
40
40
|
from ase.io import read, write
|
|
41
41
|
import shutil
|
|
42
42
|
|
|
43
|
-
__version__ = "1.3.
|
|
44
|
-
|
|
45
|
-
def read_report(folder):
|
|
46
|
-
"""
|
|
47
|
-
Read the finished calculation report
|
|
48
|
-
"""
|
|
49
|
-
repfile = os.path.join(folder, "report.yaml")
|
|
50
|
-
if not os.path.exists(repfile):
|
|
51
|
-
raise FileNotFoundError(f"file {repfile} not found")
|
|
52
|
-
|
|
53
|
-
with open(repfile, 'r') as fin:
|
|
54
|
-
data = yaml.safe_load(fin)
|
|
55
|
-
return data
|
|
43
|
+
__version__ = "1.3.11"
|
|
56
44
|
|
|
57
45
|
def _check_equal(val):
|
|
58
46
|
if not (val[0]==val[1]==val[2]):
|
|
@@ -38,12 +38,13 @@ from ase.io import read
|
|
|
38
38
|
|
|
39
39
|
#Constants
|
|
40
40
|
h = const.physical_constants["Planck constant in eV/Hz"][0]
|
|
41
|
+
hJ = const.physical_constants["Planck constant"][0]
|
|
41
42
|
hbar = h/(2*np.pi)
|
|
42
43
|
kb = const.physical_constants["Boltzmann constant in eV/K"][0]
|
|
43
44
|
kbJ = const.physical_constants["Boltzmann constant"][0]
|
|
44
45
|
Na = const.physical_constants["Avogadro constant"][0]
|
|
45
46
|
eV2J = const.eV
|
|
46
|
-
|
|
47
|
+
J2eV = 6.242E18
|
|
47
48
|
|
|
48
49
|
#--------------------------------------------------------------------
|
|
49
50
|
# TI PATH INTEGRATION ROUTINES
|
|
@@ -469,23 +470,18 @@ def get_einstein_crystal_fe(
|
|
|
469
470
|
calc,
|
|
470
471
|
vol,
|
|
471
472
|
k,
|
|
472
|
-
cm_correction=True
|
|
473
|
+
cm_correction=True,
|
|
474
|
+
return_contributions=False):
|
|
473
475
|
"""
|
|
474
476
|
Get the free energy of einstein crystal
|
|
475
477
|
|
|
476
478
|
Parameters
|
|
477
479
|
----------
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
natoms : int
|
|
482
|
-
no of atoms in the system
|
|
483
|
-
|
|
484
|
-
mass : float
|
|
485
|
-
units - g/mol
|
|
480
|
+
calc : Calculation object
|
|
481
|
+
contains all input parameters
|
|
486
482
|
|
|
487
|
-
|
|
488
|
-
|
|
483
|
+
vol : float
|
|
484
|
+
converged volume per atom
|
|
489
485
|
|
|
490
486
|
k : spring constant, float
|
|
491
487
|
units - eV/Angstrom^2
|
|
@@ -493,40 +489,77 @@ def get_einstein_crystal_fe(
|
|
|
493
489
|
cm_correction : bool, optional, default - True
|
|
494
490
|
add the centre of mass correction to free energy
|
|
495
491
|
|
|
492
|
+
return_contributions: bool, optional, default - True
|
|
493
|
+
If True, return individual contributions to the reference free energy.
|
|
494
|
+
|
|
496
495
|
Returns
|
|
497
496
|
-------
|
|
498
|
-
|
|
499
|
-
free energy of
|
|
497
|
+
F_tot : float
|
|
498
|
+
total free energy of reference crystal
|
|
499
|
+
|
|
500
|
+
F_e : float
|
|
501
|
+
Free energy of Einstein crystal without centre of mass correction. Only if `return_contributions` is True.
|
|
502
|
+
|
|
503
|
+
F_cm : float
|
|
504
|
+
centre of mass correction. Only if `return_contributions` is True.
|
|
505
|
+
|
|
506
|
+
Notes
|
|
507
|
+
-----
|
|
508
|
+
The equations for free energy of Einstein crystal and centre of mass correction are from https://doi.org/10.1063/5.0044833.
|
|
500
509
|
|
|
501
510
|
"""
|
|
502
|
-
#
|
|
503
|
-
|
|
504
|
-
mass = (mass/Na)*1E-3
|
|
505
|
-
natoms = np.sum([calc._element_dict[x]['count'] for x in calc.element])
|
|
506
|
-
concentration = np.array([calc._element_dict[x]['composition'] for x in calc.element])
|
|
511
|
+
#temperature
|
|
512
|
+
temp = calc._temperature
|
|
507
513
|
|
|
508
|
-
#
|
|
509
|
-
|
|
510
|
-
omega = np.sqrt(k/mass)
|
|
514
|
+
#natoms
|
|
515
|
+
natoms = np.sum([calc._element_dict[x]['count'] for x in calc.element])
|
|
511
516
|
|
|
512
517
|
#convert a to m3
|
|
513
518
|
vol = vol*1E-30
|
|
514
519
|
|
|
515
|
-
|
|
516
|
-
|
|
520
|
+
#whats the beta
|
|
521
|
+
beta = (1/(kbJ*temp))
|
|
517
522
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
F_harm = F_harm + F_cm
|
|
523
|
+
#create an array of mass
|
|
524
|
+
mass = []
|
|
525
|
+
for x in calc.element:
|
|
526
|
+
for count in range(calc._element_dict[x]['count']):
|
|
527
|
+
mass.append(calc._element_dict[x]['mass'])
|
|
528
|
+
mass = np.array(mass)
|
|
529
|
+
|
|
530
|
+
#convert mass to kg
|
|
531
|
+
mass = (mass/Na)*1E-3
|
|
528
532
|
|
|
529
|
-
|
|
533
|
+
#create an array of k as well
|
|
534
|
+
karr = []
|
|
535
|
+
for c, x in enumerate(calc.element):
|
|
536
|
+
for count in range(calc._element_dict[x]['count']):
|
|
537
|
+
karr.append(k[c])
|
|
538
|
+
k = np.array(karr)
|
|
539
|
+
#convert k from ev/A2 to J/m2
|
|
540
|
+
k = k*(eV2J/1E-20)
|
|
541
|
+
|
|
542
|
+
#fe of Einstein crystal
|
|
543
|
+
Z_e = ((beta**2*k*hJ**2)/(4*np.pi**2*mass))**1.5
|
|
544
|
+
F_e = np.log(Z_e)
|
|
545
|
+
F_e = kb*temp*np.sum(F_e)/natoms #*J2eV #convert back to eV
|
|
546
|
+
|
|
547
|
+
#now get the cm correction
|
|
548
|
+
if cm_correction:
|
|
549
|
+
mass_sum = np.sum(mass)
|
|
550
|
+
mu = mass/mass_sum
|
|
551
|
+
mu2_over_k = mu**2/k
|
|
552
|
+
mu2_over_k_sum = np.sum(mu2_over_k)
|
|
553
|
+
prefactor = vol
|
|
554
|
+
F_cm = np.log(prefactor*(beta/(2*np.pi*mu2_over_k_sum))**1.5)
|
|
555
|
+
F_cm = kb*temp*F_cm/natoms #convert to eV
|
|
556
|
+
else:
|
|
557
|
+
F_cm = 0
|
|
558
|
+
|
|
559
|
+
F_tot = F_e - F_cm
|
|
560
|
+
if return_contributions:
|
|
561
|
+
return F_e, -F_cm
|
|
562
|
+
return F_tot
|
|
530
563
|
|
|
531
564
|
#--------------------------------------------------------------------
|
|
532
565
|
# REF. STATE ROUTINES: LIQUID
|
|
@@ -163,6 +163,8 @@ class Phase:
|
|
|
163
163
|
|
|
164
164
|
self.ferr = 0
|
|
165
165
|
self.fref = 0
|
|
166
|
+
self.feinstein = 0
|
|
167
|
+
self.fcm = 0
|
|
166
168
|
self.fideal = 0
|
|
167
169
|
|
|
168
170
|
self.w = 0
|
|
@@ -682,6 +684,8 @@ class Phase:
|
|
|
682
684
|
report["results"]["free_energy"] = float(self.fe)
|
|
683
685
|
report["results"]["error"] = float(self.ferr)
|
|
684
686
|
report["results"]["reference_system"] = float(self.fref)
|
|
687
|
+
report["results"]["einstein_crystal"] = float(self.feinstein)
|
|
688
|
+
report["results"]["com_correction"] = float(self.fcm)
|
|
685
689
|
report["results"]["work"] = float(self.w)
|
|
686
690
|
report["results"]["pv"] = float(self.pv)
|
|
687
691
|
report["results"]["unit"] = "eV/atom"
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import numpy as np
|
|
3
|
+
import yaml
|
|
4
|
+
|
|
5
|
+
def read_report(folder):
|
|
6
|
+
"""
|
|
7
|
+
Read the finished calculation report
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
folder: string
|
|
12
|
+
folder from which calculation is to be read
|
|
13
|
+
|
|
14
|
+
Returns
|
|
15
|
+
-------
|
|
16
|
+
data: dict
|
|
17
|
+
dictionary with results
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
repfile = os.path.join(folder, "report.yaml")
|
|
21
|
+
if not os.path.exists(repfile):
|
|
22
|
+
raise FileNotFoundError(f"file {repfile} not found")
|
|
23
|
+
|
|
24
|
+
with open(repfile, 'r') as fin:
|
|
25
|
+
data = yaml.safe_load(fin)
|
|
26
|
+
return data
|
|
27
|
+
|
|
28
|
+
def _extract_error(errfile):
|
|
29
|
+
error_code = None
|
|
30
|
+
if os.path.exists(errfile):
|
|
31
|
+
with open(errfile, 'r') as fin:
|
|
32
|
+
for line in fin:
|
|
33
|
+
if 'calphy.errors' in line:
|
|
34
|
+
break
|
|
35
|
+
try:
|
|
36
|
+
error_code = line.split(':')[0].split('.')[-1]
|
|
37
|
+
except:
|
|
38
|
+
pass
|
|
39
|
+
return error_code
|
|
40
|
+
|
|
41
|
+
def gather_results(mainfolder):
|
|
42
|
+
"""
|
|
43
|
+
Gather results from all subfolders in a given folder into a Pandas DataFrame
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
mainfolder: string
|
|
48
|
+
folder where calculations are stored
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
df: pandas DataFrame
|
|
53
|
+
DataFrame with results
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
import pandas as pd
|
|
57
|
+
except ImportError:
|
|
58
|
+
raise ImportError('Please install pandas to use this function')
|
|
59
|
+
|
|
60
|
+
datadict = {}
|
|
61
|
+
datadict['mode'] = []
|
|
62
|
+
datadict['status'] = []
|
|
63
|
+
datadict['temperature'] = []
|
|
64
|
+
datadict['pressure'] = []
|
|
65
|
+
datadict['free_energy'] = []
|
|
66
|
+
datadict['reference_phase'] = []
|
|
67
|
+
datadict['error_code'] = []
|
|
68
|
+
datadict['composition'] = []
|
|
69
|
+
datadict['calculation'] = []
|
|
70
|
+
|
|
71
|
+
folders = next(os.walk(mainfolder))[1]
|
|
72
|
+
for folder in folders:
|
|
73
|
+
#adjust for pyiron folder, see
|
|
74
|
+
if folder.split('_')[-1] == 'hdf5':
|
|
75
|
+
#this could be a pyiron calc
|
|
76
|
+
withouthdf = folder.split('_hdf5')[0]
|
|
77
|
+
folder = f'{folder}/{withouthdf}'
|
|
78
|
+
|
|
79
|
+
inpfile = os.path.join(mainfolder, folder, 'input_file.yaml')
|
|
80
|
+
#print(inpfile)
|
|
81
|
+
if not os.path.exists(inpfile):
|
|
82
|
+
continue;
|
|
83
|
+
|
|
84
|
+
#ok, valid calculation, try to parse input file to get info
|
|
85
|
+
with open(inpfile, 'r') as fin:
|
|
86
|
+
inp = yaml.safe_load(fin)
|
|
87
|
+
#grab the first calculation
|
|
88
|
+
inp = inp['calculations'][0]
|
|
89
|
+
#mode
|
|
90
|
+
mode = inp['mode']
|
|
91
|
+
datadict['mode'].append(mode)
|
|
92
|
+
datadict['temperature'].append(inp['temperature'])
|
|
93
|
+
datadict['pressure'].append(inp['pressure'])
|
|
94
|
+
datadict['reference_phase'].append(inp['reference_phase'])
|
|
95
|
+
datadict['composition'].append(None)
|
|
96
|
+
datadict['calculation'].append(folder)
|
|
97
|
+
|
|
98
|
+
#check output file
|
|
99
|
+
outfile = os.path.join(mainfolder, folder, 'report.yaml')
|
|
100
|
+
datadict['error_code'].append(None)
|
|
101
|
+
|
|
102
|
+
#print(inpfile)
|
|
103
|
+
if not os.path.exists(outfile):
|
|
104
|
+
datadict['status'].append('False')
|
|
105
|
+
datadict['free_energy'].append(np.NaN)
|
|
106
|
+
#check if error file is found
|
|
107
|
+
errfile = os.path.join(os.getcwd(), mainfolder, folder+'.sub.err')
|
|
108
|
+
datadict['error_code'][-1] = _extract_error(errfile)
|
|
109
|
+
continue;
|
|
110
|
+
|
|
111
|
+
if mode in ['fe', 'alchemy', 'composition_scaling']:
|
|
112
|
+
datadict['status'].append('True')
|
|
113
|
+
|
|
114
|
+
#ok, valid calculation, try to parse input file to get info
|
|
115
|
+
with open(outfile, 'r') as fin:
|
|
116
|
+
out = yaml.safe_load(fin)
|
|
117
|
+
|
|
118
|
+
datadict['free_energy'].append(out['results']['free_energy'])
|
|
119
|
+
|
|
120
|
+
#add normal composition
|
|
121
|
+
el_arr = np.array(out['input']['element'].split(' ')).astype(str)
|
|
122
|
+
comp_arr = np.array(out['input']['concentration'].split(' ')).astype(float)
|
|
123
|
+
composition = {x:y for x,y in zip(el_arr, comp_arr)}
|
|
124
|
+
datadict['composition'][-1] = composition
|
|
125
|
+
|
|
126
|
+
if mode == 'composition_scaling':
|
|
127
|
+
#we need to update composition
|
|
128
|
+
compdict = inp['composition_scaling']['output_chemical_composition']
|
|
129
|
+
maxatoms = np.sum([val for key, val in compdict.items()])
|
|
130
|
+
for key, val in compdict.items():
|
|
131
|
+
compdict[key] = val/maxatoms
|
|
132
|
+
datadict['composition'][-1] = compdict
|
|
133
|
+
|
|
134
|
+
#parse extra info
|
|
135
|
+
if mode in ['ts', 'tscale']:
|
|
136
|
+
datafile = os.path.join(os.getcwd(), mainfolder, folder, 'temperature_sweep.dat')
|
|
137
|
+
if os.path.exists(datafile):
|
|
138
|
+
datadict['status'].append('True')
|
|
139
|
+
t, f = np.loadtxt(datafile, unpack=True, usecols=(0,1))
|
|
140
|
+
datadict['temperature'][-1] = t
|
|
141
|
+
datadict['free_energy'][-1] = f
|
|
142
|
+
else:
|
|
143
|
+
datadict['status'].append('False')
|
|
144
|
+
errfile = os.path.join(os.getcwd(), mainfolder, folder+'.sub.err')
|
|
145
|
+
datadict['error_code'][-1] = _extract_error(errfile)
|
|
146
|
+
|
|
147
|
+
df = pd.DataFrame(data=datadict)
|
|
148
|
+
return df
|
|
@@ -516,17 +516,20 @@ class Solid(cph.Phase):
|
|
|
516
516
|
Calculates the final work, energy dissipation and free energy by
|
|
517
517
|
matching with Einstein crystal
|
|
518
518
|
"""
|
|
519
|
-
|
|
519
|
+
fe, fcm = get_einstein_crystal_fe(
|
|
520
520
|
self.calc,
|
|
521
521
|
self.vol,
|
|
522
|
-
self.k
|
|
522
|
+
self.k,
|
|
523
|
+
return_contributions=True)
|
|
523
524
|
|
|
524
525
|
w, q, qerr = find_w(self.simfolder,
|
|
525
526
|
self.calc,
|
|
526
527
|
full=True,
|
|
527
528
|
solid=True)
|
|
528
529
|
|
|
529
|
-
self.fref =
|
|
530
|
+
self.fref = fe + fcm
|
|
531
|
+
self.feinstein = fe
|
|
532
|
+
self.fcm = fcm
|
|
530
533
|
self.w = w
|
|
531
534
|
self.ferr = qerr
|
|
532
535
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|