Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manual integration of the ase code from @ajmedford #50

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion dfttopif/parsers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,20 +151,33 @@ def get_output_structure(self):
or None if output file not found
'''
raise NotImplementedError


def get_input_structure(self):
'''Get the output structure, if available

Returns:
ase.Atoms - Output structure from this calculation
or None if output file not found
'''
raise NotImplementedError

def get_composition(self):
'''Get composition of output structure

Returns:
String - Composition based on output structure
'''
strc = self.get_output_structure()
if strc is None:
strc = self.get_input_structure()
counts = Counter(strc.get_chemical_symbols())
return ''.join(k if counts[k]==1 else '%s%d'%(k,counts[k]) \
for k in sorted(counts))

def get_positions(self):
strc = self.get_output_structure()
if strc is None:
strc = self.get_input_structure()
return Property(vectors=strc.positions.tolist())

def get_cutoff_energy(self):
Expand Down
92 changes: 12 additions & 80 deletions dfttopif/parsers/pwscf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
from pypif.obj.common.value import Value
from ase import Atoms
from ase.io.espresso import read_espresso_out, read_espresso_in

class PwscfParser(DFTParser):
'''
Expand Down Expand Up @@ -230,86 +231,17 @@ def get_stresses(self):
else: return None

def get_output_structure(self):
'''Determine the structure from the output'''
bohr_to_angstrom = 0.529177249

# determine the number of atoms
natoms = int(float(self._get_line('number of atoms/cell', self.outputf).split('=')[-1]))

# determine the initial lattice parameter
alat = float(self._get_line('lattice parameter (alat)', self.outputf).split('=')[-1].split()[0])

# find the initial unit cell
unit_cell = []
with open(os.path.join(self._directory, self.outputf), 'r') as fp:
for line in fp:
if "crystal axes:" in line:
for i in range(3):
unit_cell.append([float(j)*alat*bohr_to_angstrom for j in next(fp).split('(')[-1].split(')')[0].split()])
break
if len(unit_cell) == 0: raise Exception('Cannot find the initial unit cell')

# find the initial atomic coordinates
coords = [] ; atom_symbols = []
with open(os.path.join(self._directory, self.outputf), 'r') as fp:
for line in fp:
if "site n." in line and "atom" in line and "positions" in line and "alat units" in line:
for i in range(natoms):
coordline = next(fp)
atom_symbols.append(''.join([i for i in coordline.split()[1] if not i.isdigit()]))
coord_conv_factor = alat*bohr_to_angstrom
coords.append([float(j)*coord_conv_factor for j in coordline.rstrip().split('=')[-1].split('(')[-1].split(')')[0].split()])
break
if len(coords) == 0: raise Exception('Cannot find the initial atomic coordinates')

if type(self.is_relaxed()) == type(None):
# static run: create, populate, and return the initial structure
structure = Atoms(symbols=atom_symbols, cell=unit_cell, pbc=True)
structure.set_positions(coords)
return structure
else:
# relaxation run: update with the final structure
with open(os.path.join(self._directory, self.outputf)) as fp:
for line in fp:
if "Begin final coordinates" in line:
if 'new unit-cell volume' in next(fp):
# unit cell allowed to change
next(fp) # blank line
# get the final unit cell
unit_cell = []
cellheader = next(fp)
if 'bohr' in cellheader.lower():
cell_conv_factor = bohr_to_angstrom
elif 'angstrom' in cellheader.lower():
cell_conv_factor = 1.0
else:
alat = float(cellheader.split('alat=')[-1].replace(')', ''))
cell_conv_factor = alat*bohr_to_angstrom
for i in range(3):
unit_cell.append([float(j)*cell_conv_factor for j in next(fp).split()])
next(fp) # blank line

# get the final atomic coordinates
coordtype = next(fp).split()[-1].replace('(', '').replace(')', '')
if coordtype == 'bohr':
coord_conv_factor = bohr_to_angstrom
elif coordtype == 'angstrom' or coordtype == 'crystal':
coord_conv_factor = 1.0
else:
coord_conv_factor = alat*bohr_to_angstrom
coords = [] # reinitialize the coords
for i in range(natoms):
coordline = next(fp).split()
coords.append([float(j)*coord_conv_factor for j in coordline[1:4]])

# create, populate, and return the final structure
structure = Atoms(symbols=atom_symbols, cell=unit_cell, pbc=True)
if coordtype == 'crystal':
structure.set_scaled_positions(coords) # direct coord
else:
structure.set_positions(coords) # cartesian coord
return structure
raise Exception('Cannot find the final coordinates')
with open(os.path.join(self._directory, self.outputf)) as f:
gen = read_espresso_out(f, index=slice(-1, -2, -1))
# Get last value from generator
res = None
for res in gen:
pass
return res

def get_input_structure(self):
with open(os.path.join(self._directory, self.inputf)) as f:
return read_espresso_in(f)

def get_dos(self):
'''Find the total DOS shifted by the Fermi energy'''
Expand Down
27 changes: 16 additions & 11 deletions tests/parsers/test_pwscf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
import shutil

class TestPWSCFParser(unittest.TestCase):


def _test_similar(self, x, y, tol=1.0e9):
if x == 0.0 or y == 0.0:
self.assertLess(abs(x + y), tol)
self.assertLess(abs((x-y)/x), tol)

def get_parser(self,name):
'''Get a PwscfParser for a certain test'''
unpack_example(os.path.join('examples', 'pwscf', name+'.tar.gz'))
Expand All @@ -28,7 +33,7 @@ def test_NaF(self):
self.assertEquals('PWSCF', parser.get_name())

strc = parser.get_output_structure()
self.assertEquals(2.2713025676424632, strc.cell[0][2])
self._test_similar(2.2713025676424632, strc.cell[0][2])
self.assertEquals(['F', 'Na'], strc.get_chemical_symbols())
self.assertEquals('FNa', parser.get_composition())

Expand Down Expand Up @@ -66,7 +71,7 @@ def test_TiO2(self):
self.assertEquals('PWSCF', parser.get_name())

strc = parser.get_output_structure()
self.assertEquals(3.7373367889445048, strc.cell[0][0])
self._test_similar(7.0625424581405341, strc.cell[0][0])
self.assertEquals(['Ti', 'Ti', 'Ti', 'Ti', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O'], strc.get_chemical_symbols())
self.assertEquals('O8Ti4', parser.get_composition())

Expand Down Expand Up @@ -111,9 +116,9 @@ def test_VS2(self):
self.assertEquals('PWSCF', parser.get_name())

strc = parser.get_output_structure()
self.assertEquals(1.5862881841690908, strc.cell[0][0])
self.assertEquals(2.7475322138658069, strc.cell[1][1])
self.assertEquals(39.999449897411992, strc.cell[2][2])
self._test_similar(1.5862881841690908, strc.cell[0][0])
self._test_similar(2.7475322138658069, strc.cell[1][1])
self._test_similar(39.999449897411992, strc.cell[2][2])
self.assertEquals(['V', 'S', 'S'], strc.get_chemical_symbols())
self.assertEquals('S2V', parser.get_composition())

Expand Down Expand Up @@ -155,9 +160,9 @@ def test_pw_vdw(self):
self.assertEquals('PWSCF', parser.get_name())

strc = parser.get_output_structure()
self.assertEquals(2.46596598034, strc.cell[0][0])
self.assertEquals(2.1355881881239482, strc.cell[1][1])
self.assertEquals(6.4115115488840004, strc.cell[2][2])
self._test_similar(2.46596598034, strc.cell[0][0])
self._test_similar(2.1355881881239482, strc.cell[1][1])
self._test_similar(6.4115115488840004, strc.cell[2][2])
self.assertEquals(['C', 'C', 'C', 'C'], strc.get_chemical_symbols())
self.assertEquals('C4', parser.get_composition())

Expand Down Expand Up @@ -201,8 +206,8 @@ def test_pw_ldaU(self):
# Test the settings
self.assertEquals('PWSCF', parser.get_name())
strc = parser.get_output_structure()
self.assertEquals(2.1669808346549999, strc.cell[0][0])
self.assertEquals(4.3339616693099998, strc.cell[1][1])
self._test_similar(2.1669808346549999, strc.cell[0][0])
self._test_similar(4.3339616693099998, strc.cell[1][1])
self.assertEquals(['O', 'O', 'Fe', 'Fe'], strc.get_chemical_symbols())
self.assertEquals('Fe2O2', parser.get_composition())

Expand Down