Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

# coding: utf-8 

# Copyright (c) Pymatgen Development Team. 

# Distributed under the terms of the MIT License. 

 

from __future__ import division, unicode_literals 

 

""" 

This module implements an interface to the Henkelmann et al.'s excellent 

Fortran code for calculating a Bader charge analysis. 

 

This module depends on a compiled bader executable available in the path. 

Please download the library at http://theory.cm.utexas.edu/vasp/bader/ and 

follow the instructions to compile the executable. 

 

If you use this module, please cite the following: 

 

G. Henkelman, A. Arnaldsson, and H. Jonsson, "A fast and robust algorithm for 

Bader decomposition of charge density", Comput. Mater. Sci. 36, 254-360 (2006). 

""" 

 

from six.moves import map 

from six.moves import zip 

 

__author__ = "shyuepingong" 

__version__ = "0.1" 

__maintainer__ = "Shyue Ping Ong" 

__email__ = "shyuep@gmail.com" 

__status__ = "Beta" 

__date__ = "4/5/13" 

 

import os 

import subprocess 

import shutil 

 

from pymatgen.io.vasp.outputs import Chgcar 

from pymatgen.io.vasp.inputs import Potcar 

from monty.os.path import which 

from monty.dev import requires 

from monty.tempfile import ScratchDir 

 

 

@requires(which("bader"), 

"BaderAnalysis requires the executable bader to be in the path." 

" Please download the library at http://theory.cm.utexas" 

".edu/vasp/bader/ and compile the executable.") 

class BaderAnalysis(object): 

""" 

Bader analysis for a CHGCAR. 

 

.. attribute: data 

 

Atomic data parsed from bader analysis. Essentially a list of dicts 

of the form:: 

 

[ 

{ 

"dist": 8.769, 

"min": 0.8753, 

"charge": 7.4168, 

"y": 1.1598, 

"x": 0.0079, 

"z": 0.8348 

}, 

... 

] 

 

.. attribute: vacuum_volume 

 

Vacuum volume of the Bader analysis. 

 

.. attribute: vacuum_charge 

 

Vacuum charge of the Bader analysis. 

 

.. attribute: nelectrons 

 

Number of electrons of the Bader analysis. 

 

.. attribute: chgcar 

 

Chgcar object associated with input CHGCAR file. 

 

.. attribute: potcar 

 

Potcar object associated with POTCAR used for calculation (used for 

calculating charge transferred). 

""" 

 

 

def __init__(self, chgcar_filename, potcar_filename=None): 

""" 

Initializes the Bader caller. 

 

Args: 

chgcar_filename: The filename of the CHGCAR. 

potcar_filename: Optional: the filename of the corresponding 

POTCAR file. Used for calculating the charge transfer. If 

None, the get_charge_transfer method will raise a ValueError. 

""" 

self.chgcar = Chgcar.from_file(chgcar_filename) 

self.potcar = Potcar.from_file(potcar_filename) \ 

if potcar_filename is not None else None 

self.natoms = self.chgcar.poscar.natoms 

chgcarpath = os.path.abspath(chgcar_filename) 

 

with ScratchDir(".") as temp_dir: 

shutil.copy(chgcarpath, os.path.join(temp_dir, "CHGCAR")) 

 

rs = subprocess.Popen(["bader", "CHGCAR"], 

stdout=subprocess.PIPE, 

stdin=subprocess.PIPE, close_fds=True) 

rs.communicate() 

data = [] 

with open("ACF.dat") as f: 

raw = f.readlines() 

headers = [s.lower() for s in raw.pop(0).split()] 

raw.pop(0) 

while True: 

l = raw.pop(0).strip() 

if l.startswith("-"): 

break 

vals = map(float, l.split()[1:]) 

data.append(dict(zip(headers[1:], vals))) 

for l in raw: 

toks = l.strip().split(":") 

if toks[0] == "VACUUM CHARGE": 

self.vacuum_charge = float(toks[1]) 

elif toks[0] == "VACUUM VOLUME": 

self.vacuum_volume = float(toks[1]) 

elif toks[0] == "NUMBER OF ELECTRONS": 

self.nelectrons = float(toks[1]) 

self.data = data 

 

def get_charge(self, atom_index): 

""" 

Convenience method to get the charge on a particular atom. 

 

Args: 

atom_index: 

Index of atom. 

 

Returns: 

Charge associated with atom from the Bader analysis. 

""" 

return self.data[atom_index]["charge"] 

 

def get_charge_transfer(self, atom_index): 

""" 

Returns the charge transferred for a particular atom. Requires POTCAR 

to be supplied. 

 

Args: 

atom_index: 

Index of atom. 

 

Returns: 

Charge transfer associated with atom from the Bader analysis. 

Given by final charge on atom - nelectrons in POTCAR for 

associated atom. 

""" 

if self.potcar is None: 

raise ValueError("POTCAR must be supplied in order to calculate " 

"charge transfer!") 

potcar_indices = [] 

for i, v in enumerate(self.natoms): 

potcar_indices += [i] * v 

nelect = self.potcar[potcar_indices[atom_index]].nelectrons 

return self.data[atom_index]["charge"] - nelect 

 

def get_oxidation_state_decorated_structure(self): 

""" 

Returns an oxidation state decorated structure. 

 

Returns: 

Returns an oxidation state decorated structure. Requires POTCAR 

to be supplied. 

""" 

structure = self.chgcar.structure 

charges = [self.get_charge_transfer(i) for i in range(len(structure))] 

structure.add_oxidation_state_by_site(charges) 

return structure