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

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

# coding: utf-8 

# Copyright (c) Pymatgen Development Team. 

# Distributed under the terms of the MIT License. 

 

from __future__ import division, print_function, unicode_literals 

from __future__ import absolute_import 

 

""" 

This module provides classes and methods used to describe deformations and 

strains, including applying those deformations to structure objects and 

generating deformed structure sets for further calculations. 

""" 

 

from pymatgen.core.lattice import Lattice 

from pymatgen.analysis.elasticity.tensors import SquareTensor 

from pymatgen.symmetry.analyzer import SpacegroupAnalyzer 

import warnings 

import numpy as np 

from six.moves import zip 

 

__author__ = "Maarten de Jong" 

__copyright__ = "Copyright 2012, The Materials Project" 

__credits__ = "Joseph Montoya, Mark Asta, Anubhav Jain" 

__version__ = "1.0" 

__maintainer__ = "Joseph Montoya" 

__email__ = "montoyjh@lbl.gov" 

__status__ = "Development" 

__date__ = "March 13, 2012" 

 

 

class Deformation(SquareTensor): 

""" 

Subclass of SquareTensor that describes the deformation gradient tensor 

""" 

 

def __new__(cls, deformation_gradient): 

""" 

Create a Deformation object. Note that the constructor uses __new__ 

rather than __init__ according to the standard method of subclassing 

numpy ndarrays. 

 

Args: 

deformation_gradient (3x3 array-like): the 3x3 array-like 

representing the deformation gradient 

""" 

 

obj = SquareTensor(deformation_gradient).view(cls) 

return obj 

 

def __array_finalize__(self, obj): 

if obj is None: 

return 

 

def check_independent(self): 

""" 

checks to determine whether the deformation matrix represents an 

independent deformation, raises a ValueError if not. If so, returns 

the indices of the deformation gradient entry representing the 

independent component 

""" 

indices = list(zip(*np.asarray(self - np.eye(3)).nonzero())) 

if len(indices) != 1: 

raise ValueError("One and only one independent deformation" 

"must be applied.") 

return indices[0] 

 

@property 

def green_lagrange_strain(self): 

""" 

calculates the euler-lagrange strain from 

the deformation gradient 

""" 

return Strain.from_deformation(self) 

 

def apply_to_structure(self, structure): 

""" 

Apply the deformation gradient to a structure. 

 

Args: 

structure (Structure object): the structure object to 

be modified by the deformation 

""" 

def_struct = structure.copy() 

def_struct.modify_lattice(Lattice(np.dot(def_struct.lattice.matrix, 

self))) 

return def_struct 

 

@classmethod 

def from_index_amount(cls, matrixpos, amt): 

""" 

Factory method for constructing a Deformation object 

from a matrix position and amount 

 

Args: 

matrixpos (tuple): tuple corresponding the matrix position to 

have a perturbation added 

amt (float): amount to add to the identity matrix at position 

matrixpos 

""" 

f = np.identity(3) 

f[matrixpos] += amt 

return cls(f) 

 

 

class DeformedStructureSet(object): 

""" 

class that generates a set of independently deformed structures that 

can be used to calculate linear stress-strain response 

""" 

 

def __init__(self, rlxd_str, nd=0.01, ns=0.08, 

num_norm=4, num_shear=4, symmetry=False): 

""" 

constructs the deformed geometries of a structure. Generates 

m + n deformed structures according to the supplied parameters. 

 

Args: 

rlxd_str (structure): structure to undergo deformation, if 

fitting elastic tensor is desired, should be a geometry 

optimized structure 

nd (float): maximum perturbation applied to normal deformation 

ns (float): maximum perturbation applied to shear deformation 

m (int): number of deformation structures to generate for 

normal deformation, must be even 

n (int): number of deformation structures to generate for 

shear deformation, must be even 

""" 

 

if num_norm % 2 != 0: 

raise ValueError("Number of normal deformations (num_norm)" 

" must be even.") 

if num_shear % 2 != 0: 

raise ValueError("Number of shear deformations (num_shear)" 

" must be even.") 

 

norm_deformations = np.linspace(-nd, nd, num=num_norm + 1) 

norm_deformations = norm_deformations[norm_deformations.nonzero()] 

shear_deformations = np.linspace(-ns, ns, num=num_shear + 1) 

shear_deformations = shear_deformations[shear_deformations.nonzero()] 

 

self.undeformed_structure = rlxd_str 

self.deformations = [] 

self.def_structs = [] 

 

# Generate deformations 

for ind in [(0, 0), (1, 1), (2, 2)]: 

for amount in norm_deformations: 

defo = Deformation.from_index_amount(ind, amount) 

self.deformations.append(defo) 

 

for ind in [(0, 1), (0, 2), (1, 2)]: 

for amount in shear_deformations: 

defo = Deformation.from_index_amount(ind, amount) 

self.deformations.append(defo) 

 

# Perform symmetry reduction if specified 

if symmetry: 

sga = SpacegroupAnalyzer(self.undeformed_structure, tol = 0.1) 

symm_ops = sga.get_symmetry_operations(cartesian=True) 

self.deformations = symm_reduce(symm_ops, self.deformations) 

 

self.def_structs = [defo.apply_to_structure(rlxd_str) 

for defo in self.deformations] 

 

def symm_reduce(self, symm_ops, deformation_list, tolerance = 1e-2): 

""" 

Checks list of deformation gradient tensors for symmetrical 

equivalents and returns a new list with reduntant ones removed 

 

Args:  

symm_ops (list of SymmOps): list of SymmOps objects with which  

to check the list of deformation tensors for duplicates 

deformation_list (list of Deformations): list of deformation 

gradient objects to check for duplicates 

tolerance (float): tolerance for assigning equal defo. gradients 

""" 

unique_defos = [] 

for defo in deformation_list: 

in_unique = False 

for op in symm_ops: 

if np.any([(np.abs(defo - defo.transform(symm_op)) < tol).all() 

for unique_defo in unique_defos]): 

in_unique = True 

break 

if not in_unique: 

unique_defos += [defo] 

return unique_defos 

 

def __iter__(self): 

return iter(self.def_structs) 

 

def as_strain_dict(self): 

""" 

Returns dictionary of deformed structures indexed by independent 

strain objects in accordance with legacy behavior of elasticity 

package 

""" 

strains = [IndependentStrain(defo) for defo in self.deformations] 

return dict(zip(strains, self.def_structs)) 

 

 

class Strain(SquareTensor): 

""" 

Subclass of SquareTensor that describes the Green-Lagrange strain tensor. 

""" 

 

def __new__(cls, strain_matrix, dfm=None): 

""" 

Create a Strain object. Note that the constructor uses __new__ 

rather than __init__ according to the standard method of 

subclassing numpy ndarrays. Note also that the default constructor 

does not include the deformation gradient 

 

Args: 

strain_matrix (3x3 array-like): the 3x3 array-like 

representing the Green-Lagrange strain 

""" 

 

obj = SquareTensor(strain_matrix).view(cls) 

obj._dfm = dfm 

if not obj.is_symmetric(): 

raise ValueError("Strain objects must be initialized " 

"with a symmetric array-like.") 

 

if dfm is None: 

warnings.warn("Constructing a strain object without a deformation " 

"matrix makes many methods unusable. Use " 

"Strain.from_deformation to construct a Strain object" 

" from a deformation gradient.") 

elif (np.array(dfm) - obj < 1e-5).all(): 

warnings.warn("Warning: deformation matrix does not correspond " 

"to input strain_matrix value") 

return obj 

 

def __array_finalize__(self, obj): 

if obj is None: 

return 

self.rank = getattr(obj, "rank", None) 

self._dfm = getattr(obj, "_dfm", None) 

 

@classmethod 

def from_deformation(cls, deformation): 

""" 

Factory method that returns a Strain object from a deformation 

gradient 

 

Args: 

deformation (3x3 array-like): 

""" 

dfm = Deformation(deformation) 

return cls(0.5 * (np.dot(dfm.trans, dfm) - np.eye(3)), dfm) 

 

@property 

def deformation_matrix(self): 

""" 

returns the deformation matrix 

""" 

return self._dfm 

 

@property 

def independent_deformation(self): 

""" 

determines whether the deformation matrix represents an 

independent deformation, raises a value error if not. 

Returns the index of the deformation gradient corresponding 

to the independent deformation 

""" 

if self._dfm is None: 

raise ValueError("No deformation matrix supplied " 

"for this strain tensor.") 

return self._dfm.check_independent() 

 

@property 

def voigt(self): 

""" 

translates a strain tensor into a voigt notation vector 

""" 

return [self[0, 0], self[1, 1], self[2, 2], 

2. * self[1, 2], 2. * self[0, 2], 2. * self[0, 1]] 

 

 

class IndependentStrain(Strain): 

""" 

Class for independent strains intended for use with old Materials Project 

elasticity workflow. Note that the default constructor constructs from 

a deformation matrix, rather than an array representing the strain, to 

emulate the legacy behavior. 

""" 

 

def __new__(cls, deformation_gradient): 

""" 

Create an Independent Strain object. Note that the constructor uses 

__new__ rather than __init__ according to the standard method of 

subclassing numpy ndarrays. Note also that, unlike the Strain class, 

the default constructor of IndependentStrain takes the deformation 

gradient as input, rather than an array representing the Green-Lagrange 

strain. 

 

Args: 

deformation_gradient (3x3 array-like): the 3x3 array-like 

representing the deformation gradient 

""" 

 

obj = Strain.from_deformation(deformation_gradient).view(cls) 

(obj._i, obj._j) = obj.independent_deformation 

return obj 

 

def __array_finalize__(self, obj): 

if obj is None: 

return 

self._dfm = getattr(obj, "_dfm", None) 

self._i = getattr(obj, "_i", None) 

self._j = getattr(obj, "_j", None) 

 

@property 

def i(self): 

return self._i 

 

@property 

def j(self): 

return self._j