Skip to content

Commit 20d4cf9

Browse files
authored
Merge pull request #161 from ICAMS/main
update branch
2 parents a7c3171 + 29fd253 commit 20d4cf9

12 files changed

+239
-43
lines changed

.bumpversion.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 1.3.10
2+
current_version = 1.3.12
33
commit = True
44
tag = True
55

calphy/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from calphy.alchemy import Alchemy
55
from calphy.routines import MeltingTemp
66

7-
__version__ = "1.3.10"
7+
__version__ = "1.3.12"
88

99
def addtest(a,b):
1010
return a+b

calphy/input.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
from ase.io import read, write
4141
import shutil
4242

43-
__version__ = "1.3.10"
43+
__version__ = "1.3.12"
4444

4545
def _check_equal(val):
4646
if not (val[0]==val[1]==val[2]):

calphy/integrators.py

+68-35
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@
3838

3939
#Constants
4040
h = const.physical_constants["Planck constant in eV/Hz"][0]
41+
hJ = const.physical_constants["Planck constant"][0]
4142
hbar = h/(2*np.pi)
4243
kb = const.physical_constants["Boltzmann constant in eV/K"][0]
4344
kbJ = const.physical_constants["Boltzmann constant"][0]
4445
Na = const.physical_constants["Avogadro constant"][0]
4546
eV2J = const.eV
46-
47+
J2eV = 6.242E18
4748

4849
#--------------------------------------------------------------------
4950
# TI PATH INTEGRATION ROUTINES
@@ -469,64 +470,96 @@ def get_einstein_crystal_fe(
469470
calc,
470471
vol,
471472
k,
472-
cm_correction=True):
473+
cm_correction=True,
474+
return_contributions=False):
473475
"""
474476
Get the free energy of einstein crystal
475477
476478
Parameters
477479
----------
478-
temp : temperature, float
479-
units - K
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
486482
487-
a : lattice constant, float
488-
units - Angstrom
483+
vol : float
484+
converged volume per atom
489485
490486
k : spring constant, float
491487
units - eV/Angstrom^2
492488
493489
cm_correction : bool, optional, default - True
494490
add the centre of mass correction to free energy
495491
492+
return_contributions: bool, optional, default - True
493+
If True, return individual contributions to the reference free energy.
494+
496495
Returns
497496
-------
498-
fe : float
499-
free energy of Einstein crystal
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.
500509
501510
"""
502-
#convert mass first for single particle in kg
503-
mass = np.array([calc._element_dict[x]['mass'] for x in calc.element])
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
507513

508-
#convert k from ev/A2 to J/m2
509-
k = np.array(k)*(eV2J/1E-20)
510-
omega = np.sqrt(k/mass)
514+
#natoms
515+
natoms = np.sum([calc._element_dict[x]['count'] for x in calc.element])
511516

512517
#convert a to m3
513518
vol = vol*1E-30
514519

515-
F_harm = 0
516-
F_cm = 0
520+
#whats the beta
521+
beta = (1/(kbJ*temp))
517522

518-
for count, om in enumerate(omega):
519-
if concentration[count] > 0:
520-
F_harm += concentration[count]*np.log((hbar*om)/(kb*calc._temperature))
521-
if cm_correction:
522-
F_cm += np.log((natoms*concentration[count]/vol)*(2*np.pi*kbJ*calc._temperature/(natoms*concentration[count]*k[count]))**1.5)
523-
#F_cm = 0
524-
F_harm = 3*kb*calc._temperature*F_harm
525-
F_cm = (kb*calc._temperature/natoms)*F_cm
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
528532

529-
return F_harm
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
530563

531564
#--------------------------------------------------------------------
532565
# REF. STATE ROUTINES: LIQUID

calphy/phase.py

+4
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ def __init__(self, calculation=None, simfolder=None, log_to_screen=False):
163163

164164
self.ferr = 0
165165
self.fref = 0
166+
self.feinstein = 0
167+
self.fcm = 0
166168
self.fideal = 0
167169

168170
self.w = 0
@@ -682,6 +684,8 @@ def submit_report(self, extra_dict=None):
682684
report["results"]["free_energy"] = float(self.fe)
683685
report["results"]["error"] = float(self.ferr)
684686
report["results"]["reference_system"] = float(self.fref)
687+
report["results"]["einstein_crystal"] = float(self.feinstein)
688+
report["results"]["com_correction"] = float(self.fcm)
685689
report["results"]["work"] = float(self.w)
686690
report["results"]["pv"] = float(self.pv)
687691
report["results"]["unit"] = "eV/atom"

calphy/postprocessing.py

+82-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import os
22
import numpy as np
33
import yaml
4+
import matplotlib.pyplot as plt
5+
import warnings
46

57
def read_report(folder):
68
"""
@@ -145,4 +147,83 @@ def gather_results(mainfolder):
145147
datadict['error_code'][-1] = _extract_error(errfile)
146148

147149
df = pd.DataFrame(data=datadict)
148-
return df
150+
return df
151+
152+
def find_transition_temperature(folder1, folder2, fit_order=4, plot=True):
153+
"""
154+
Find transition temperature where free energy of two phases are equal.
155+
156+
Parameters
157+
----------
158+
folder1: string
159+
directory with temperature scale calculation
160+
161+
folder2: string
162+
directory with temperature scale calculation
163+
164+
fit_order: int, optional
165+
default 4. Order for polynomial fit of temperature vs free energy
166+
167+
plot: bool, optional
168+
default True. Plot the results.
169+
"""
170+
file1 = os.path.join(folder1, 'temperature_sweep.dat')
171+
file2 = os.path.join(folder2, 'temperature_sweep.dat')
172+
if not os.path.exists(file1):
173+
raise FileNotFoundError(f'{file1} does not exist')
174+
if not os.path.exists(file2):
175+
raise FileNotFoundError(f'{file2} does not exist')
176+
177+
t1, f1 = np.loadtxt(file1, unpack=True, usecols=(0,1))
178+
t2, f2 = np.loadtxt(file2, unpack=True, usecols=(0,1))
179+
180+
#do some fitting to determine temps
181+
t1min = np.min(t1)
182+
t2min = np.min(t2)
183+
t1max = np.max(t1)
184+
t2max = np.max(t2)
185+
186+
tmin = np.min([t1min, t2min])
187+
tmax = np.max([t1max, t2max])
188+
189+
#warn about extrapolation
190+
if not t1min == t2min:
191+
warnings.warn(f'free energy is being extrapolated!')
192+
if not t1max == t2max:
193+
warnings.warn(f'free energy is being extrapolated!')
194+
195+
#now fit
196+
f1fit = np.polyfit(t1, f1, fit_order)
197+
f2fit = np.polyfit(t2, f2, fit_order)
198+
199+
#reevaluate over the new range
200+
fit_t = np.arange(tmin, tmax+1, 1)
201+
fit_f1 = np.polyval(f1fit, fit_t)
202+
fit_f2 = np.polyval(f2fit, fit_t)
203+
204+
#now evaluate the intersection temp
205+
arg = np.argsort(np.abs(fit_f1-fit_f2))[0]
206+
transition_temp = fit_t[arg]
207+
208+
#warn if the temperature is shady
209+
if np.abs(transition_temp-tmin) < 1E-3:
210+
warnings.warn('It is likely there is no intersection of free energies')
211+
elif np.abs(transition_temp-tmax) < 1E-3:
212+
warnings.warn('It is likely there is no intersection of free energies')
213+
214+
#plot
215+
if plot:
216+
c1lo = '#ef9a9a'
217+
c1hi = '#b71c1c'
218+
c2lo = '#90caf9'
219+
c2hi = '#0d47a1'
220+
221+
plt.plot(fit_t, fit_f1, color=c1lo, label=f'{folder1} fit')
222+
plt.plot(fit_t, fit_f2, color=c2lo, label=f'{folder2} fit')
223+
plt.plot(t1, f1, color=c1hi, label=folder1, ls='dashed')
224+
plt.plot(t2, f2, color=c2hi, label=folder2, ls='dashed')
225+
plt.axvline(transition_temp, ls='dashed', c='#37474f')
226+
plt.ylabel('Free energy (eV/atom)')
227+
plt.xlabel('Temperature (K)')
228+
plt.legend(frameon=False)
229+
return transition_temp

calphy/solid.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -516,17 +516,20 @@ def thermodynamic_integration(self):
516516
Calculates the final work, energy dissipation and free energy by
517517
matching with Einstein crystal
518518
"""
519-
f1 = get_einstein_crystal_fe(
519+
fe, fcm = get_einstein_crystal_fe(
520520
self.calc,
521521
self.vol,
522-
self.k)
522+
self.k,
523+
return_contributions=True)
523524

524525
w, q, qerr = find_w(self.simfolder,
525526
self.calc,
526527
full=True,
527528
solid=True)
528529

529-
self.fref = f1
530+
self.fref = fe + fcm
531+
self.feinstein = fe
532+
self.fcm = fcm
530533
self.w = w
531534
self.ferr = qerr
532535

docs/source/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ publication <https://journals.aps.org/prmaterials/abstract/10.1103/PhysRevMateri
4747
Using calphy <running_calphy/running_calphy>
4848
inputfile
4949
examples
50+
research
5051
calphy
5152
faqs
5253
prologue/extending

docs/source/inputfile.md

+53
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,59 @@ folder_prefix: set1
546546

547547
Prefix string to be added to folder names for calculation. Folders for calculations in calphy are named as `mode-lattice-temperature-pressure`. Therefore, if more than one calculation is run with the same parameters, they will be overwritten. To prevent this, `folder_prefix` can be used. If `folder_prefix` is provided, the folders will be named as `folder_prefix-mode-lattice-temperature-pressure`.
548548

549+
550+
---
551+
552+
(script_mode)=
553+
#### `script_mode`
554+
555+
_type_: bool \
556+
_default_: False \
557+
_example_:
558+
```
559+
script_mode: False
560+
```
561+
562+
If True, a LAMMPS executable script is written and executed instead of the library interface of LAMMPS.
563+
Works only with `reference_phase: solid`, and `mode: fe`.
564+
Needs specification of [`lammps_executable`](lammps_executable) and [`mpi_executable`](mpi_executable).
565+
566+
567+
---
568+
569+
(lammps_executable)=
570+
#### `lammps_executable`
571+
572+
_type_: string \
573+
_default_: None \
574+
_example_:
575+
```
576+
lammps_executable: lmp_mpi
577+
```
578+
579+
LAMMPS executable to run the calculations with.
580+
Works only with `reference_phase: solid`, and `mode: fe`.
581+
Works only if [`script_mode`](script_mode) is `True`.
582+
583+
584+
585+
---
586+
587+
(mpi_executable)=
588+
#### `mpi_executable`
589+
590+
_type_: string \
591+
_default_: None \
592+
_example_:
593+
```
594+
mpi_executable: mpiexec
595+
```
596+
597+
MPI executable to run the LAMMPS with.
598+
Works only with `reference_phase: solid`, and `mode: fe`.
599+
Works only if [`script_mode`](script_mode) is `True`.
600+
601+
549602
---
550603
---
551604

docs/source/prologue/acknowledgements.md

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ We acknowledge the following people for their contribution to calphy:
2424

2525
- Abril Azócar Guzmán for the design of calphy logo
2626

27+
- Marvin Poul for numerous fixes, improvements, and for the help in fixing centre of mass corrections for multiple species
28+
29+
- Sebastian Havens for the singularity recipe
30+
31+
2732
The development of this module was started at the [Interdisciplinary Centre for Advanced
2833
Materials Simulation](http://www.icams.de/content), at the [Ruhr
2934
University Bochum](https://www.ruhr-uni-bochum.de/en), Germany. Current development is carried out at the [Max-Planck-Institut für Eisenforschung GmbH](https://www.mpie.de/).

0 commit comments

Comments
 (0)