Skip to content

Commit

Permalink
Merge pull request #423 from nisse3000/master
Browse files Browse the repository at this point in the history
Added Xr class to I/O module
  • Loading branch information
shyuep committed Jun 24, 2016
2 parents e51101b + 0277e13 commit e0aba38
Show file tree
Hide file tree
Showing 3 changed files with 276 additions and 0 deletions.
77 changes: 77 additions & 0 deletions pymatgen/io/tests/test_xr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# coding: utf-8
# Copyright (c) Pymatgen Development Team.
# Distributed under the terms of the MIT License.

from __future__ import division, unicode_literals

__author__ = "Nils Edvin Richard Zimmermann"
__copyright__ = "Copyright 2016, The Materials Project"
__version__ = "0.1"
__maintainer__ = "Nils Edvin Richard Zimmermann"
__email__ = "nils.e.r.zimmermann@gmail.com"
__date__ = "June 23, 2016"

import unittest2 as unittest
import os

from pymatgen.io.xr import Xr
from pymatgen.io.vasp.inputs import Poscar
from pymatgen.core.structure import Structure

test_dir = os.path.join(os.path.dirname(__file__), "..", "..", "..",
'test_files')

class XrTest(unittest.TestCase):

def setUp(self):

filepath = os.path.join(test_dir, 'POSCAR')
p = Poscar.from_file(filepath)
self.xr = Xr(p.structure)

def test_str(self):
expected_string = """pymatgen 10.4118 6.0672 4.7595
90.000 90.000 90.000
24 0
0 Fe4 P4 O16
1 Fe 2.2773 4.5504 2.2601
2 Fe 2.9285 1.5168 4.6399
3 Fe 7.4832 4.5504 0.1196
4 Fe 8.1344 1.5168 2.4994
5 P 0.9851 1.5168 1.9906
6 P 4.2208 4.5504 4.3704
7 P 6.1910 1.5168 0.3891
8 P 9.4267 4.5504 2.7689
9 O 0.4516 4.5504 3.3656
10 O 1.0062 1.5168 3.5283
11 O 1.7253 0.2795 1.3583
12 O 1.7253 2.7541 1.3583
13 O 3.4806 3.3131 3.7380
14 O 3.4806 5.7876 3.7380
15 O 4.1997 4.5504 1.1486
16 O 4.7543 1.5168 0.9859
17 O 5.6575 4.5504 3.7736
18 O 6.2121 1.5168 3.6109
19 O 6.9312 0.2795 1.0215
20 O 6.9312 2.7541 1.0215
21 O 8.6864 3.3131 3.4012
22 O 8.6864 5.7876 3.4012
23 O 9.4055 4.5504 1.2312
24 O 9.9602 1.5168 1.3939
10.4118 0.0000 0.0000
0.0000 6.0672 0.0000
0.0000 0.0000 4.7595
10.4118 0.0000 0.0000
0.0000 6.0672 0.0000
0.0000 0.0000 4.7595"""
self.assertEqual(str(self.xr), expected_string)

def test_from_file(self):
filename = os.path.join(test_dir, "EDI.xr")
xr = Xr.from_file(filename)
self.assertIsInstance(xr.structure, Structure)
xr2 = Xr.from_file(filename, use_cores=False)
self.assertIsInstance(xr2.structure, Structure)

if __name__ == "__main__":
unittest.main()
164 changes: 164 additions & 0 deletions pymatgen/io/xr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# coding: utf-8
# Copyright (c) Pymatgen Development Team.
# Distributed under the terms of the MIT License.

from __future__ import division, unicode_literals

"""
This module provides input and output mechanisms
for the xr file format, which is a modified CSSR
file format and, for example, used in GULP.
In particular, the module makes it easy
to remove shell positions from relaxations
that employed core-shell models.
"""


__author__ = "Nils Edvin Richard Zimmermann"
__copyright__ = "Copyright 2016, The Materials Project"
__version__ = "0.1"
__maintainer__ = "Nils Edvin Richard Zimmermann"
__email__ = "nils.e.r.zimmermann@gmail.com"
__date__ = "June 23, 2016"

import re

from six.moves import map
import numpy as np

from monty.io import zopen
from math import fabs
from pymatgen.core.lattice import Lattice
from pymatgen.core.structure import Structure

class Xr(object):
"""
Basic object for working with xr files.
Args:
structure (Structure/IStructure): Structure object to create the
Xr object.
"""

def __init__(self, structure):
if not structure.is_ordered:
raise ValueError("Xr file can only be constructed from ordered "
"structure")
self.structure = structure

def __str__(self):
output = ["pymatgen {:.4f} {:.4f} {:.4f}"
.format(*self.structure.lattice.abc),
"{:.3f} {:.3f} {:.3f}"
.format(*self.structure.lattice.angles),
"{} 0".format(len(self.structure)),
"0 {}".format(self.structure.formula)]
# There are actually 10 more fields per site
# in a typical xr file from GULP, for example.
for i, site in enumerate(self.structure.sites):
output.append("{} {} {:.4f} {:.4f} {:.4f}"
.format(i + 1, site.specie, site.x, site.y, site.z))
mat = self.structure.lattice.matrix
for i in range(2):
for j in range(3):
output.append("{:.4f} {:.4f} {:.4f}".format(
mat[j][0], mat[j][1], mat[j][2]))
return "\n".join(output)

def write_file(self, filename):
"""
Write out an xr file.
Args:
filename (str): name of the file to write to.
"""
with zopen(filename, 'wt') as f:
f.write(str(self) + "\n")

@staticmethod
def from_string(string, use_cores=True, thresh=1.e-4):
"""
Creates an Xr object from a string representation.
Args:
string (str): string representation of an Xr object.
use_cores (bool): use core positions and discard shell
positions if set to True (default). Otherwise,
use shell positions and discard core positions.
thresh (float): relative threshold for consistency check
between cell parameters (lengths and angles) from
header information and cell vectors, respectively.
Returns:
xr (Xr): Xr object corresponding to the input
string representation.
"""
lines = string.split("\n")
toks = lines[0].split()
lengths = [float(toks[i]) for i in range(1, len(toks))]
toks = lines[1].split()
angles = [float(i) for i in toks[0:3]]
toks = lines[2].split()
nsites = int(toks[0])
mat = np.zeros((3,3), dtype=float)
for i in range(3):
toks = lines[4+nsites+i].split()
toks2 = lines[4+nsites+i+3].split()
for j, item in enumerate(toks):
if item != toks2[j]:
raise RuntimeError("expected both matrices"
" to be the same in xr file")
mat[i] = np.array([float(w) for w in toks])
lat = Lattice(mat)
if fabs(lat.a-lengths[0])/fabs(lat.a) > thresh or \
fabs(lat.b-lengths[1])/fabs(lat.b) > thresh or \
fabs(lat.c-lengths[2])/fabs(lat.c) > thresh or \
fabs(lat.alpha-angles[0])/fabs(lat.alpha) > thresh or \
fabs(lat.beta-angles[1])/fabs(lat.beta) > thresh or \
fabs(lat.gamma-angles[2])/fabs(lat.gamma) > thresh:
raise RuntimeError("cell parameters in header ("+str(lengths)+\
", "+str(angles)+") are not consistent with Cartesian"+\
" lattice vectors ("+str(lat.abc)+", "+\
str(lat.angles)+")")
# Ignore line w/ index 3.
sp = []
coords = []
for j in range(nsites):
m = re.match("\d+\s+(\w+)\s+([0-9\-\.]+)\s+([0-9\-\.]+)\s+" +
"([0-9\-\.]+)", lines[4+j].strip())
if m:
tmp_sp = m.group(1)
if use_cores and tmp_sp[len(tmp_sp)-2:] == "_s":
continue
if not use_cores and tmp_sp[len(tmp_sp)-2:] == "_c":
continue
if tmp_sp[len(tmp_sp)-2] == "_":
sp.append(tmp_sp[0:len(tmp_sp)-2])
else:
sp.append(tmp_sp)
coords.append([float(m.group(i)) for i in range(2, 5)])
return Xr(Structure(lat, sp, coords, coords_are_cartesian=True))

@staticmethod
def from_file(filename, use_cores=True, thresh=1.e-4):
"""
Reads an xr-formatted file to create an Xr object.
Args:
filename (str): name of file to read from.
use_cores (bool): use core positions and discard shell
positions if set to True (default). Otherwise,
use shell positions and discard core positions.
thresh (float): relative threshold for consistency check
between cell parameters (lengths and angles) from
header information and cell vectors, respectively.
Returns:
xr (Xr): Xr object corresponding to the input
file.
"""
with zopen(filename, "rt") as f:
return Xr.from_string(
f.read(), use_cores=use_cores,
thresh=thresh)

35 changes: 35 additions & 0 deletions test_files/EDI.xr
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
somestring 6.8774 6.8774 6.3558
90.0 90.0 90.0
25 1 EDI structure
1 bulk
1 Si 1.84246 -0.00000 0.75253 0 0 0 0 0 0 0 0 4.000 0.00
2 Si 6.85980 1.86010 5.58528 0 0 0 0 0 0 0 0 4.000 0.00
3 Si 4.99970 -0.00000 0.75253 0 0 0 0 0 0 0 0 4.000 0.00
4 Si 6.85980 5.01733 5.58528 0 0 0 0 0 0 0 0 4.000 0.00
5 Si 6.85980 6.87743 3.16890 0 0 0 0 0 0 0 0 4.000 0.00
6 O_c 1.29725 1.31488 6.34680 0 0 0 0 0 0 0 0 0.869 74.92
7 O_s 1.21428 1.23192 6.34680 0 0 0 0 0 0 0 0 -2.869 74.92
8 O_c 5.54491 1.31488 6.34680 0 0 0 0 0 0 0 0 0.869 74.92
9 O_s 5.62788 1.23192 6.34680 0 0 0 0 0 0 0 0 -2.869 74.92
10 O_c 5.54491 5.56255 6.34680 0 0 0 0 0 0 0 0 0.869 74.92
11 O_s 5.62788 5.64551 6.34680 0 0 0 0 0 0 0 0 -2.869 74.92
12 O_c 1.29725 5.56255 6.34680 0 0 0 0 0 0 0 0 0.869 74.92
13 O_s 1.21428 5.64551 6.34680 0 0 0 0 0 0 0 0 -2.869 74.92
14 O_c 1.31620 6.87743 2.27055 0 0 0 0 0 0 0 0 0.869 74.92
15 O_s 1.22765 6.87743 2.20123 0 0 0 0 0 0 0 0 -2.869 74.92
16 O_c 6.85980 1.33384 4.06726 0 0 0 0 0 0 0 0 0.869 74.92
17 O_s 6.85980 1.24529 4.13658 0 0 0 0 0 0 0 0 -2.869 74.92
18 O_c 5.52596 6.87743 2.27055 0 0 0 0 0 0 0 0 0.869 74.92
19 O_s 5.61451 6.87743 2.20123 0 0 0 0 0 0 0 0 -2.869 74.92
20 O_c 6.85980 5.54359 4.06726 0 0 0 0 0 0 0 0 0.869 74.92
21 O_s 6.85980 5.63214 4.13658 0 0 0 0 0 0 0 0 -2.869 74.92
22 O_c 3.42108 -0.00000 0.75105 0 0 0 0 0 0 0 0 0.869 74.92
23 O_s 3.42108 -0.00000 0.75165 0 0 0 0 0 0 0 0 -2.869 74.92
24 O_c 6.85980 3.43871 5.58676 0 0 0 0 0 0 0 0 0.869 74.92
25 O_s 6.85980 3.43871 5.58616 0 0 0 0 0 0 0 0 -2.869 74.92
6.877431 0.000000 0.000000
0.000000 6.877431 -0.000000
0.000000 -0.000000 6.355796
6.877431 0.000000 0.000000
0.000000 6.877431 -0.000000
0.000000 -0.000000 6.355796

0 comments on commit e0aba38

Please sign in to comment.