Skip to content

Commit 31201c6

Browse files
committed
enh: improved PR by Thomas now one can query/set fixed for geometries
Now one can write/read the fixed status of atoms. When using lists for `fixed` argument one can easily define a simple scheme to limit some atoms vs. others. Signed-off-by: Nick Papior <[email protected]>
1 parent 3d1ebd5 commit 31201c6

File tree

3 files changed

+86
-12
lines changed

3 files changed

+86
-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: 47 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)
@@ -70,9 +80,18 @@ def write_geometry(self, geometry):
7080
self._write('Selective dynamics\n')
7181
self._write('Cartesian\n')
7282

73-
fmt = '{:18.9f}' * 3 + ' F F F\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
7493
for ia in geometry:
75-
self._write(fmt.format(*geometry.xyz[ia, :]))
94+
self._write(fmt.format(*geometry.xyz[ia, :]) + todyn(fixed[ia]))
7695

7796
@sile_fh_open(True)
7897
def read_supercell(self):
@@ -92,8 +111,15 @@ def read_supercell(self):
92111
return SuperCell(cell)
93112

94113
@sile_fh_open()
95-
def read_geometry(self):
96-
""" 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
97123
"""
98124
sc = self.read_supercell()
99125

@@ -124,9 +150,9 @@ def read_geometry(self):
124150
# Read whether this is selective or direct
125151
# Currently direct is not used
126152
opt = self.readline()
127-
#direct = True
153+
dynamics = False
128154
if opt[0] in 'Ss':
129-
#direct = False
155+
dynamics = True
130156
opt = self.readline()
131157

132158
# Check whether this is in fractional or direct
@@ -137,18 +163,27 @@ def read_geometry(self):
137163

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

141-
xyz = np.empty([na, 3], np.float64)
169+
xyz = _a.emptyd([na, 3])
142170
for ia in range(na):
143-
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+
144176
if cart:
145177
# The unit of the coordinates are cartesian
146178
xyz *= self._scale
147179
else:
148-
xyz = np.dot(xyz, sc.cell)
180+
xyz = xyz.dot(sc.cell)
149181

150182
# The POT/CONT-CAR does not contain information on the atomic species
151-
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
152187

153188
def ArgumentParser(self, p=None, *args, **kwargs):
154189
""" 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)