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

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

# coding: utf-8 

# Copyright (c) Pymatgen Development Team. 

# Distributed under the terms of the MIT License. 

 

from __future__ import unicode_literals 

 

""" 

Module containing class to create an ion 

""" 

 

__author__ = "Sai Jayaraman" 

__copyright__ = "Copyright 2012, The Materials Project" 

__version__ = "0.0" 

__maintainer__ = "Sai Jayaraman" 

__email__ = "sjayaram@mit.edu" 

__status__ = "Production" 

__date__ = "Dec 10, 2012" 

 

import re 

import numpy as np 

 

from pymatgen.core.composition import Composition 

from monty.json import MSONable 

from pymatgen.util.string_utils import formula_double_format 

 

 

class Ion(MSONable): 

""" 

Basic ion object. It is just a Composition object with an additional 

variable to store charge. 

The net charge can either be represented as Mn++, or Mn+2, or Mn[2+]. 

Note the order of the sign and magnitude in each representation. 

""" 

def __init__(self, composition, charge=0.0, properties=None): 

""" 

Flexible Ion construction, similar to Composition. 

For more information, please see pymatgen.core.Composition 

""" 

self._composition = Composition(composition) 

self._charge = charge 

self._properties = properties if properties else {} 

 

def __getattr__(self, a): 

if a in self._properties: 

return self._properties[a] 

try: 

return getattr(self._composition, a) 

except: 

raise AttributeError(a) 

 

@staticmethod 

def from_formula(formula): 

charge = 0.0 

f = formula 

m = re.search(r"\[([^\[\]]+)\]", f) 

if m: 

m_chg = re.search("([\.\d]*)([+-])", m.group(1)) 

if m_chg: 

if m_chg.group(1) != "": 

charge += float(m_chg.group(1)) * \ 

(float(m_chg.group(2) + "1")) 

else: 

charge += float(m_chg.group(2) + "1") 

f = f.replace(m.group(), "", 1) 

m = re.search(r"\(aq\)", f) 

if m: 

f = f.replace(m.group(), "", 1) 

for m_chg in re.finditer("([+-])([\.\d]*)", f): 

sign = m_chg.group(1) 

sgn = float(str(sign + "1")) 

if m_chg.group(2).strip() != "": 

charge += float(m_chg.group(2)) * sgn 

else: 

charge += sgn 

f = f.replace(m_chg.group(), "", 1) 

composition = Composition(f) 

return Ion(composition, charge) 

 

@property 

def formula(self): 

""" 

Returns a formula string, with elements sorted by electronegativity, 

e.g., Li4 Fe4 P4 O16. 

""" 

formula = self._composition.formula 

chg_str = "" 

if self._charge > 0: 

chg_str = " +" + formula_double_format(self._charge, False) 

elif self._charge < 0: 

chg_str = " " + formula_double_format(self._charge, False) 

return formula + chg_str 

 

@property 

def anonymized_formula(self): 

""" 

An anonymized formula. Appends charge to the end 

of anonymized composition 

""" 

anon_formula = self._composition.anonymized_formula 

chg = self._charge 

chg_str = "" 

if chg > 0: 

chg_str += ("{}{}".format('+', str(int(chg)))) 

elif chg < 0: 

chg_str += ("{}{}".format('-', str(int(np.abs(chg))))) 

return anon_formula + chg_str 

 

@property 

def reduced_formula(self): 

""" 

Returns a reduced formula string with appended charge. 

""" 

reduced_formula = self._composition.reduced_formula 

charge = self._charge / float(self._composition. 

get_reduced_composition_and_factor()[1]) 

if charge > 0: 

if abs(charge) == 1: 

chg_str = "[+]" 

else: 

chg_str = "[" + formula_double_format(charge, False) + "+]" 

elif charge < 0: 

if abs(charge) == 1: 

chg_str = "[-]" 

else: 

chg_str = "[{}-]".format(formula_double_format(abs(charge), 

False)) 

else: 

chg_str = "(aq)" 

return reduced_formula + chg_str 

 

@property 

def alphabetical_formula(self): 

""" 

Returns a reduced formula string with appended charge 

""" 

alph_formula = self._composition.alphabetical_formula 

chg_str = "" 

if self._charge > 0: 

chg_str = " +" + formula_double_format(self._charge, False) 

elif self._charge < 0: 

chg_str = " " + formula_double_format(self._charge, False) 

return alph_formula + chg_str 

 

@property 

def charge(self): 

""" 

Charge of the ion 

""" 

return self._charge 

 

@property 

def composition(self): 

""" 

Return composition object 

""" 

return self._composition 

 

def as_dict(self): 

""" 

Returns: 

dict with composition, as well as charge 

""" 

d = self._composition.as_dict() 

d['charge'] = self._charge 

return d 

 

@classmethod 

def from_dict(cls, d): 

""" 

Generates an ion object from a dict created by as_dict(). 

 

Args: 

d: 

{symbol: amount} dict. 

""" 

# composition = Composition.from_dict(d['composition']) 

charge = d['charge'] 

composition = Composition({i: d[i] for i in d if i != 'charge'}) 

return Ion(composition, charge) 

 

@property 

def to_reduced_dict(self): 

""" 

Returns: 

dict with element symbol and reduced amount e.g., 

{"Fe": 2.0, "O":3.0}. 

""" 

reduced_formula = self._composition.reduced_formula 

c = Composition(reduced_formula) 

d = c.as_dict() 

d['charge'] = self._charge 

return d 

 

def __eq__(self, other): 

if self.composition != other.composition: 

return False 

if self.charge != other.charge: 

return False 

return True 

 

def __ne__(self, other): 

return not self.__eq__(other) 

 

def __add__(self, other): 

""" 

Addition of two ions. 

""" 

new_composition = self.composition + other.composition 

new_charge = self.charge + other.charge 

return Ion(new_composition, new_charge) 

 

def __sub__(self, other): 

""" 

Subtraction of two ions 

""" 

new_composition = self.composition - other.composition 

new_charge = self.charge - other.charge 

return Ion(new_composition, new_charge) 

 

def __mul__(self, other): 

""" 

Multiplication of an Ion with a factor 

""" 

new_composition = self.composition * other 

new_charge = self.charge * other 

return Ion(new_composition, new_charge) 

 

def __hash__(self): 

#for now, just use the composition hash code. 

return self._composition.__hash__() 

 

def __len__(self): 

return len(self._composition) 

 

def __str__(self): 

return self.formula 

 

def __repr__(self): 

return "Ion: " + self.formula 

 

def __getitem__(self, el): 

return self._composition.get(el, 0)