Skip to content

Commit 1856986

Browse files
committed
Merge branch '185-vasp'
2 parents 59aad10 + 31201c6 commit 1856986

File tree

3 files changed

+87
-12
lines changed

3 files changed

+87
-12
lines changed

CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
0.9.9
22
=====
33

4+
- Enabled VASP *CAR files to write/read dynamic specifications
5+
46
- Enabled xarray.DataArray returning from BrillouinZone objects #182
57

68
- Several improvements to outSileSiesta.read_scf (thanks to Pol Febrer)

sisl/io/vasp/car.py

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from ..sile import *
66

77
# Import the geometry object
8+
import sisl._array as _a
89
from sisl.messages import warn
910
from sisl import Geometry, PeriodicTable, Atom, SuperCell
1011

@@ -22,13 +23,22 @@ def _setup(self, *args, **kwargs):
2223
self._scale = 1.
2324

2425
@sile_fh_open()
25-
def write_geometry(self, geometry):
26-
""" Writes the geometry to the contained file
26+
def write_geometry(self, geometry, fixed=False):
27+
r""" Writes the geometry to the contained file
2728
2829
Parameters
2930
----------
3031
geometry : Geometry
3132
geometry to be written to the file
33+
fixed : bool or list, optional
34+
define which atoms to be fixed in the VASP run
35+
36+
Examples
37+
--------
38+
>>> car = carSileVASP('POSCAR', 'w')
39+
>>> geom = geom.graphene()
40+
>>> geom.write(car, fixed=False) # fix none
41+
>>> geom.write(car, fixed=[True, (False, True, False)]) # fix 1st and y coordinate of 2nd
3242
"""
3343
# Check that we can write to the file
3444
sile_raise_write(self)
@@ -67,11 +77,21 @@ def write_geometry(self, geometry):
6777
self._write(fmt.format(*s))
6878
fmt = ' {:d}' * len(d) + '\n'
6979
self._write(fmt.format(*d))
80+
self._write('Selective dynamics\n')
7081
self._write('Cartesian\n')
7182

72-
fmt = '{:18.9f}' * 3 + '\n'
83+
if isinstance(fixed, bool):
84+
fixed = [fixed] * len(geometry)
85+
86+
b2s = {True: 'T', False: 'F'}
87+
def todyn(fix):
88+
if isinstance(fix, bool):
89+
return '{0} {0} {0}\n'.format(b2s[fix])
90+
return '{} {} {}\n'.format(b2s[fix[0]], b2s[fix[1]], b2s[fix[2]])
91+
92+
fmt = '{:18.9f} ' * 3
7393
for ia in geometry:
74-
self._write(fmt.format(*geometry.xyz[ia, :]))
94+
self._write(fmt.format(*geometry.xyz[ia, :]) + todyn(fixed[ia]))
7595

7696
@sile_fh_open(True)
7797
def read_supercell(self):
@@ -91,8 +111,15 @@ def read_supercell(self):
91111
return SuperCell(cell)
92112

93113
@sile_fh_open()
94-
def read_geometry(self):
95-
""" Returns Geometry object from the CONTCAR/POSCAR file
114+
def read_geometry(self, ret_fixed=False):
115+
r""" Returns Geometry object from the CONTCAR/POSCAR file
116+
117+
Possibly also return the dynamics (if present)
118+
119+
Parameters
120+
----------
121+
ret_fixed : bool, optional
122+
also read selective dynamics (if present), if not, a list of False will be returned
96123
"""
97124
sc = self.read_supercell()
98125

@@ -123,9 +150,9 @@ def read_geometry(self):
123150
# Read whether this is selective or direct
124151
# Currently direct is not used
125152
opt = self.readline()
126-
#direct = True
153+
dynamics = False
127154
if opt[0] in 'Ss':
128-
#direct = False
155+
dynamics = True
129156
opt = self.readline()
130157

131158
# Check whether this is in fractional or direct
@@ -136,18 +163,27 @@ def read_geometry(self):
136163

137164
# Number of atoms
138165
na = len(atom)
166+
# pre-create the fixed list
167+
fixed = [[False] * 3] * na
139168

140-
xyz = np.empty([na, 3], np.float64)
169+
xyz = _a.emptyd([na, 3])
141170
for ia in range(na):
142-
xyz[ia, :] = list(map(float, self.readline().split()[:3]))
171+
line = self.readline().split()
172+
xyz[ia, :] = list(map(float, line[:3]))
173+
if dynamics:
174+
fixed[ia] = list(map(lambda x: x.lower() == 't', line[3:6]))
175+
143176
if cart:
144177
# The unit of the coordinates are cartesian
145178
xyz *= self._scale
146179
else:
147-
xyz = np.dot(xyz, sc.cell)
180+
xyz = xyz.dot(sc.cell)
148181

149182
# The POT/CONT-CAR does not contain information on the atomic species
150-
return Geometry(xyz=xyz, atom=atom, sc=sc)
183+
geom = Geometry(xyz=xyz, atom=atom, sc=sc)
184+
if ret_fixed:
185+
return geom, np.array(fixed, dtype=np.bool_)
186+
return geom
151187

152188
def ArgumentParser(self, p=None, *args, **kwargs):
153189
""" Returns the arguments that is available for this Sile """

sisl/io/vasp/tests/test_car.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,40 @@ def test_geometry_car_allsame(sisl_tmp):
3737
geom.write(carSileVASP(f, 'w'))
3838

3939
assert carSileVASP(f).read_geometry() == geom
40+
41+
42+
def test_geometry_car_allsame(sisl_tmp):
43+
f = sisl_tmp('test_read_write.POSCAR', _dir)
44+
45+
atoms = Atom[1]
46+
xyz = np.random.rand(10, 3)
47+
geom = Geometry(xyz, atoms, 100)
48+
49+
geom.write(carSileVASP(f, 'w'))
50+
51+
assert carSileVASP(f).read_geometry() == geom
52+
53+
54+
def test_geometry_car_fixed(sisl_tmp):
55+
f = sisl_tmp('test_fixed.POSCAR', _dir)
56+
57+
atoms = Atom[1]
58+
xyz = np.random.rand(10, 3)
59+
geom = Geometry(xyz, atoms, 100)
60+
61+
read = carSileVASP(f)
62+
63+
geom.write(carSileVASP(f, 'w'))
64+
g, fix = read.read_geometry(ret_fixed=True)
65+
assert not np.any(fix)
66+
67+
geom.write(carSileVASP(f, 'w'), fixed=True)
68+
g, fix = read.read_geometry(ret_fixed=True)
69+
assert np.all(fix)
70+
71+
fixed = [False] * len(geom)
72+
fixed[0] = [True, False, True]
73+
geom.write(carSileVASP(f, 'w'), fixed=fixed)
74+
g, fix = read.read_geometry(ret_fixed=True)
75+
assert np.array_equal(fixed[0], fix[0])
76+
assert not np.any(fix[1:])

0 commit comments

Comments
 (0)