calphy 1.3.9__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.9/calphy.egg-info → calphy-1.3.11}/PKG-INFO +1 -1
- {calphy-1.3.9 → calphy-1.3.11}/calphy/__init__.py +1 -1
- {calphy-1.3.9 → calphy-1.3.11}/calphy/input.py +1 -13
- {calphy-1.3.9 → calphy-1.3.11}/calphy/integrators.py +72 -36
- {calphy-1.3.9 → calphy-1.3.11}/calphy/phase.py +4 -0
- calphy-1.3.11/calphy/postprocessing.py +148 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy/scheduler.py +3 -1
- {calphy-1.3.9 → calphy-1.3.11}/calphy/solid.py +6 -3
- {calphy-1.3.9 → calphy-1.3.11/calphy.egg-info}/PKG-INFO +1 -1
- {calphy-1.3.9 → calphy-1.3.11}/calphy.egg-info/SOURCES.txt +1 -0
- {calphy-1.3.9 → calphy-1.3.11}/setup.py +1 -1
- {calphy-1.3.9 → calphy-1.3.11}/LICENSE +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/MANIFEST.in +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/README.md +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy/alchemy.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy/clitools.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy/composition_transformation.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy/errors.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy/helpers.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy/kernel.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy/liquid.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy/phase_diagram.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy/queuekernel.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy/routines.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy/splines.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy/utils.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy.egg-info/dependency_links.txt +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy.egg-info/entry_points.txt +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy.egg-info/not-zip-safe +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy.egg-info/requires.txt +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/calphy.egg-info/top_level.txt +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/setup.cfg +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/tests/test_helpers.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/tests/test_integrators.py +0 -0
- {calphy-1.3.9 → calphy-1.3.11}/tests/test_options.py +0 -0
- {calphy-1.3.9 → 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]):
|
|
@@ -28,19 +28,23 @@ import math
|
|
|
28
28
|
import os
|
|
29
29
|
import warnings
|
|
30
30
|
from calphy.splines import splines, sum_spline1, sum_spline25, sum_spline50, sum_spline75, sum_spline100
|
|
31
|
-
|
|
31
|
+
try:
|
|
32
|
+
from scipy.integrate import cumtrapz
|
|
33
|
+
except ImportError:
|
|
34
|
+
from scipy.integrate import cumulative_trapezoid as cumtrapz
|
|
32
35
|
from tqdm import tqdm
|
|
33
36
|
import pyscal3.core as pc
|
|
34
37
|
from ase.io import read
|
|
35
38
|
|
|
36
39
|
#Constants
|
|
37
40
|
h = const.physical_constants["Planck constant in eV/Hz"][0]
|
|
41
|
+
hJ = const.physical_constants["Planck constant"][0]
|
|
38
42
|
hbar = h/(2*np.pi)
|
|
39
43
|
kb = const.physical_constants["Boltzmann constant in eV/K"][0]
|
|
40
44
|
kbJ = const.physical_constants["Boltzmann constant"][0]
|
|
41
45
|
Na = const.physical_constants["Avogadro constant"][0]
|
|
42
46
|
eV2J = const.eV
|
|
43
|
-
|
|
47
|
+
J2eV = 6.242E18
|
|
44
48
|
|
|
45
49
|
#--------------------------------------------------------------------
|
|
46
50
|
# TI PATH INTEGRATION ROUTINES
|
|
@@ -466,23 +470,18 @@ def get_einstein_crystal_fe(
|
|
|
466
470
|
calc,
|
|
467
471
|
vol,
|
|
468
472
|
k,
|
|
469
|
-
cm_correction=True
|
|
473
|
+
cm_correction=True,
|
|
474
|
+
return_contributions=False):
|
|
470
475
|
"""
|
|
471
476
|
Get the free energy of einstein crystal
|
|
472
477
|
|
|
473
478
|
Parameters
|
|
474
479
|
----------
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
natoms : int
|
|
479
|
-
no of atoms in the system
|
|
480
|
-
|
|
481
|
-
mass : float
|
|
482
|
-
units - g/mol
|
|
480
|
+
calc : Calculation object
|
|
481
|
+
contains all input parameters
|
|
483
482
|
|
|
484
|
-
|
|
485
|
-
|
|
483
|
+
vol : float
|
|
484
|
+
converged volume per atom
|
|
486
485
|
|
|
487
486
|
k : spring constant, float
|
|
488
487
|
units - eV/Angstrom^2
|
|
@@ -490,40 +489,77 @@ def get_einstein_crystal_fe(
|
|
|
490
489
|
cm_correction : bool, optional, default - True
|
|
491
490
|
add the centre of mass correction to free energy
|
|
492
491
|
|
|
492
|
+
return_contributions: bool, optional, default - True
|
|
493
|
+
If True, return individual contributions to the reference free energy.
|
|
494
|
+
|
|
493
495
|
Returns
|
|
494
496
|
-------
|
|
495
|
-
|
|
496
|
-
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.
|
|
497
509
|
|
|
498
510
|
"""
|
|
499
|
-
#
|
|
500
|
-
|
|
501
|
-
mass = (mass/Na)*1E-3
|
|
502
|
-
natoms = np.sum([calc._element_dict[x]['count'] for x in calc.element])
|
|
503
|
-
concentration = np.array([calc._element_dict[x]['composition'] for x in calc.element])
|
|
511
|
+
#temperature
|
|
512
|
+
temp = calc._temperature
|
|
504
513
|
|
|
505
|
-
#
|
|
506
|
-
|
|
507
|
-
omega = np.sqrt(k/mass)
|
|
514
|
+
#natoms
|
|
515
|
+
natoms = np.sum([calc._element_dict[x]['count'] for x in calc.element])
|
|
508
516
|
|
|
509
517
|
#convert a to m3
|
|
510
518
|
vol = vol*1E-30
|
|
511
519
|
|
|
512
|
-
|
|
513
|
-
|
|
520
|
+
#whats the beta
|
|
521
|
+
beta = (1/(kbJ*temp))
|
|
514
522
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
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
|
|
525
532
|
|
|
526
|
-
|
|
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
|
|
527
563
|
|
|
528
564
|
#--------------------------------------------------------------------
|
|
529
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
|
|
@@ -90,6 +90,7 @@ class SLURM:
|
|
|
90
90
|
"""
|
|
91
91
|
self.queueoptions = {"scheduler": "slurm",
|
|
92
92
|
"jobname": "tis",
|
|
93
|
+
"queuename": None,
|
|
93
94
|
"walltime": "23:59:00",
|
|
94
95
|
"memory": "3GB",
|
|
95
96
|
"cores": cores,
|
|
@@ -125,7 +126,8 @@ class SLURM:
|
|
|
125
126
|
#write the main header options
|
|
126
127
|
fout.write("#SBATCH --job-name=%s\n" %self.queueoptions["jobname"])
|
|
127
128
|
fout.write("#SBATCH --time=%s\n" %self.queueoptions["walltime"])
|
|
128
|
-
|
|
129
|
+
if self.queueoptions["queuename"] is not None:
|
|
130
|
+
fout.write("#SBATCH --partition=%s\n"%self.queueoptions["queuename"])
|
|
129
131
|
fout.write("#SBATCH --ntasks=%s\n" %str(self.queueoptions["cores"]))
|
|
130
132
|
fout.write("#SBATCH --mem-per-cpu=%s\n"%self.queueoptions["memory"])
|
|
131
133
|
fout.write("#SBATCH --hint=%s\n" %self.queueoptions["hint"])
|
|
@@ -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
|