J.R. Kermode, L. Ben-Bashat, F. Atrash, J.J. Cilliers, D. Sherman and A. De Vita, Macroscopic scattering of cracks initiated at single impurity atoms. Nat. Commun. 4, 2441 (2013).
Primary:
Secondary:
Longer term benefits:
spglib
, phonopy
) using minimal ASE-compatible APIQUIP
has a full, deep Python interface, quippy
, auto-generated from Fortran code using f90wrap
, giving access to all public subroutines, derived types (classes) and data.html
or .pdf
, or as executable Python script%pylab inline
import numpy as np
from chemview import enable_notebook
from matscipy.visualise import view
enable_notebook()
from ase.lattice import bulk
from ase.optimize import LBFGSLineSearch
from quippy.potential import Potential
si = bulk('Si', a=5.44, cubic=True)
sw_pot = Potential('IP SW') # call into Fortran code
si.set_calculator(sw_pot)
e_bulk_per_atom = si.get_potential_energy()/len(si)
vac = si * (3, 3, 3)
del vac[len(vac)/2]
vac.set_calculator(sw_pot)
p0 = vac.get_positions()
opt = LBFGSLineSearch(vac)
opt.run(fmax=1e-3)
p1 = vac.get_positions()
u = p1 - p0
e_vac = vac.get_potential_energy()
print 'SW vacancy formation energy', e_vac - e_bulk_per_atom*len(vac), 'eV'
view(vac, np.sqrt(u**2).sum(axis=1), bonds=False)
SocketCalculator
from matscipy
package keeps code running, feeding it new configurations via POSIX sockets (local or remote)import distutils.spawn as spawn
from matscipy.socketcalc import SocketCalculator, VaspClient
from matscipy.checkpoint import CheckpointCalculator
from ase.lattice import bulk
from ase.optimize import FIRE
mpirun = spawn.find_executable('mpirun')
vasp = spawn.find_executable('vasp')
vasp_client = VaspClient(client_id=0, npj=2, ppn=12,
exe=vasp, mpirun=mpirun, parmode='mpi',
lwave=False, lcharg=False, ibrion=13,
xc='PBE', kpts=[2,2,2])
vasp = SocketCalculator(vasp_client)
chk_vasp = CheckpointCalculator(vasp, 'vasp_checkpoint.db')
si = bulk('Si', a=5.44, cubic=True)
si.set_calculator(chk_vasp)
e_bulk_per_atom = si.get_potential_energy()/len(si)
vac3 = si.copy()
del vac3[0]
vac3.set_calculator(chk_vasp)
opt = FIRE(vac3)
opt.run(fmax=1e-3)
e_vac3 = vac3.get_potential_energy()
print 'VASP vacancy formation energy', e_vac3 - e_bulk_per_atom*len(vac3), 'eV'
f90wrap
adds derived type support to f2py
¶f90wrap
addresses this by generating an additional layer of wrappersbader
code¶f90wrap
to automatically generate a deep Python interface with very little manual workGeneration and compilation of wrappers:
f90wrap -v -k kind_map -I init.py -m bader \
kind_mod.f90 matrix_mod.f90 \
ions_mod.f90 options_mod.f90 charge_mod.f90 \
chgcar_mod.f90 cube_mod.f90 io_mod.f90 \
bader_mod.f90 voronoi_mod.f90 multipole_mod.f90
f2py-f90wrap -c -m _bader f90wrap_*.f90 -L. -lbader
from gpaw import restart
si, gpaw = restart('si-vac.gpw')
rho = gpaw.get_pseudo_density()
atom = 5
plot(si.positions[:, 0], si.positions[:, 1], 'k.', ms=20)
plot(si.positions[atom, 0], si.positions[5, 1], 'g.', ms=20)
imshow(rho[:,:,0], extent=[0, si.cell[0,0], 0, si.cell[1,1]])
import bader
bdr = bader.bader(si, rho)
bdr.nvols
# collect Bader volumes associated with atom #5
mask = np.zeros_like(rho, dtype=bool)
for v in (bdr.nnion == atom+1).nonzero()[0]:
mask[bdr.volnum == v+1] = True
plot(si.positions[:, 0], si.positions[:, 1], 'k.', ms=20)
plot(si.positions[atom, 0], si.positions[5, 1], 'g.', ms=20)
imshow(rho[:,:,0], extent=[0, si.cell[0,0], 0, si.cell[1,1]])
imshow(mask[:,:,0], extent=[0, si.cell[0,0], 0, si.cell[1,1]], alpha=.6)
f90wrap
can now wrap large and complex codes like the Castep electronic structure code, providing deep access to internal data on-the-fly make python
Restricted set of source files currently wrapped, can be easily expanded.
Utility: constants.F90 algor.F90
comms.serial.F90
io.F90 trace.F90
license.F90 buildinfo.f90
Fundamental: parameters.F90 cell.F90 basis.F90
ion.F90 density.F90 wave.F90
Functional: model.F90 electronic.F90 firstd.f90
Already far too much to wrap by hand!
current_cell
, etc.Unit_Cell
current_cell%ionic_positions
io_abort()
raises RuntimeError
exception, allowing post mortem debuggingimport castep
#castep.
#castep.cell.Unit_Cell.
castep.model.model_wave_read?
from ase.lattice.cubic import Diamond
atoms = Diamond('Si')
calc = castep.calculator.CastepCalculator(atoms=atoms)
atoms.set_calculator(calc)
e = atoms.get_potential_energy()
f = atoms.get_forces()
print 'Energy', e, 'eV'
print 'Forces (eV/A):'
print f
#calc.model.eigenvalues
#calc.model.wvfn.coeffs
#calc.model.cell.ionic_positions.T
#calc.model.wvfn.
#calc.parameters.cut_off_energy
figsize(8,6)
plot(castep.ion.get_array_core_radial_charge())
plot(castep.ion.get_array_atomic_radial_charge())
ylim(-0.5,0.5)
# grid points, in Angstrom
real_grid = (castep.basis.get_array_r_real_grid()*
castep.io.io_atomic_to_unit(1.0, 'ang'))
resolution = [castep.basis.get_ngx(),
castep.basis.get_ngy(),
castep.basis.get_ngz()]
origin = np.array([real_grid[i, :].min() for i in range(3)])
extent = np.array([real_grid[i, :].max() for i in range(3)]) - origin
# charge density resulting from SCF
den = calc.model.den.real_charge.copy()
den3 = (den.reshape(resolution, order='F') /
castep.basis.get_total_fine_grid_points())
# visualise system with isosurface of charge density at 0.002
viewer = view(atoms)
viewer.add_isosurface_grid_data(den3, origin, extent, resolution,
isolevel=0.002, color=0x0000ff,
style='solid')
viewer
from display import ListTable
from bader import bader
bdr = bader(atoms, den3)
rows = ListTable()
rows.append(['<b>{0}</b>'.format(hd) for hd in ['Ion', 'Charge', 'Volume']])
for i, (chg, vol) in enumerate(zip(bdr.ionchg, bdr.ionvol)):
rows.append(['{0:.2f}'.format(d) for d in [i, chg, vol] ])
rows
So far this is just analysis/post-processing, but could easily go beyond this and steer calculations based on results of e.g. Bader analysis.
We can move the ions and continue the calculation without having to restart electronic minimisation from scratch (or do any I/O of .check
files etc.). Here's how the core of the electronic minimisation is coded in Python, almost entirely calling auto-generated routines:
new_cell = atoms_to_cell(atoms, kpts=self.kpts)
castep.cell.copy(new_cell, self.current_cell)
self.model.wvfn.have_beta_phi = False
castep.wave.wave_sorthonormalise(self.model.wvfn)
self.model.total_energy, self.model.converged = \
electronic_minimisation(self.model.wvfn,
self.model.den,
self.model.occ,
self.model.eigenvalues,
self.model.fermi_energy)
self.results['energy'] = io_atomic_to_unit(self.model.total_energy, 'eV')
castep.wave.wave_orthogonalise?
Use embedded Castep efficiently as a standard ASE calculator, giving access to all of the existing high-level algorithms: geometry optimisation, NEB, basin hopping, etc.
from ase.optimize import LBFGS
atoms.rattle(0.01)
opt = LBFGS(atoms)
opt.run(fmax=0.1)
Having a Python interface makes it quick to try out new high-level algorithms.
from ase.lattice import bulk
import castep
import preconpy.lbfgs as lbfgs
import preconpy.precon as precon
from preconpy.utils import LoggingCalculator
atoms = bulk('Si', cubic=True)
s = atoms.get_scaled_positions()
s[:, 0] *= 0.98
atoms.set_scaled_positions(s)
initial_atoms = atoms
log_calc = LoggingCalculator(None)
for precon, label in zip([None, precon.Exp(A=3, use_pyamg=False)],
['No preconditioner', 'Exp preconditioner']):
print label
atoms = initial_atoms.copy()
calc = castep.calculator.CastepCalculator(atoms=atoms)
log_calc.calculator = calc
log_calc.label = label
atoms.set_calculator(log_calc)
opt = lbfgs.LBFGS(atoms,
precon=precon,
use_line_search=False)
opt.run(fmax=1e-2)
QUIP
developed with Gábor Csányi, Noam Bernstein, et al.matscipy
https://github.com/libAtoms/matscipy, developed with Lars Pastewka, KITf90wrap
https://github.com/jameskermode/f90wrapchemview
https://github.com/gabrielelanaro/chemview/ by Gabriele Lanaro