Package mmLib :: Module UnitCell
[hide private]
[frames] | no frames]

Source Code for Module mmLib.UnitCell

  1  ## Copyright 2002-2010 by PyMMLib Development Group (see AUTHORS file) 
  2  ## This code is part of the PyMMLib distribution and governed by 
  3  ## its license.  Please see the LICENSE file that should have been 
  4  ## included as part of this package. 
  5  """Classes for handling unit cell transformation. 
  6  """ 
  7  import math 
  8   
  9  try: 
 10      import numpy 
 11      try: 
 12          from numpy.oldnumeric import linear_algebra as linalg 
 13      except ImportError: 
 14          from numpy.linalg import old as linalg 
 15  except ImportError: 
 16      import NumericCompat as numpy 
 17      from NumericCompat import linalg 
 18   
 19  import AtomMath 
 20  import SpaceGroups 
 21   
 22   
23 -class UnitCell(object):
24 """Class for storing and performing calculations on unit cell 25 parameters. The constructor expects alpha, beta, and gamma to be in 26 degrees, but converts them to radians. Set angle_units = "rad" if 27 the alpha, beta, and gamma are already in radians. 28 """
29 - def __init__(self, 30 a = 1.0, 31 b = 1.0, 32 c = 1.0, 33 alpha = 90.0, 34 beta = 90.0, 35 gamma = 90.0, 36 space_group = "P1", 37 angle_units = "deg"):
38 39 assert angle_units == "deg" or angle_units == "rad" 40 41 self.a = a 42 self.b = b 43 self.c = c 44 45 if angle_units == "deg": 46 self.alpha = math.radians(alpha) 47 self.beta = math.radians(beta) 48 self.gamma = math.radians(gamma) 49 elif angle_units == "rad": 50 self.alpha = alpha 51 self.beta = beta 52 self.gamma = gamma 53 54 self.space_group = SpaceGroups.GetSpaceGroup(space_group) 55 self.orth_to_frac = self.calc_fractionalization_matrix() 56 self.frac_to_orth = self.calc_orthogonalization_matrix() 57 58 ## check our math! 59 assert numpy.allclose(self.orth_to_frac, linalg.inverse(self.frac_to_orth))
60
61 - def __str__(self):
62 alpha = math.degrees(self.alpha) 63 beta = math.degrees(self.beta) 64 gamma = math.degrees(self.gamma) 65 66 return "UnitCell(a=%f, b=%f, c=%f, alpha=%f, beta=%f, gamma=%f)" % ( 67 self.a, self.b, self.c, alpha, beta, gamma)
68
69 - def calc_alpha_deg(self):
70 """Returns the alpha angle in degrees. 71 """ 72 return math.degrees(self.alpha)
73
74 - def calc_beta_deg(self):
75 """Returns the beta angle in degrees. 76 """ 77 return math.degrees(self.beta)
78
79 - def calc_gamma_deg(self):
80 """Returns the gamma angle in degrees. 81 """ 82 return math.degrees(self.gamma)
83
84 - def calc_v(self):
85 """Calculates the volume of the rhombohedral created by the 86 unit vectors a1/|a1|, a2/|a2|, a3/|a3|. 87 """ 88 cos_alpha = math.cos(self.alpha) 89 cos_beta = math.cos(self.beta) 90 cos_gamma = math.cos(self.gamma) 91 return math.sqrt(1 - 92 (cos_alpha * cos_alpha) - 93 (cos_beta * cos_beta) - 94 (cos_gamma * cos_gamma) + 95 (2 * cos_alpha * cos_beta * cos_gamma) )
96
97 - def calc_volume(self):
98 """Calculates the volume of the unit cell. 99 """ 100 return self.a * self.b * self.c * self.calc_v()
101
103 """Corresponding reciprocal unit cell. 104 """ 105 V = self.calc_volume() 106 107 sin_alpha = math.sin(self.alpha) 108 sin_beta = math.sin(self.beta) 109 sin_gamma = math.sin(self.gamma) 110 111 cos_alpha = math.cos(self.alpha) 112 cos_beta = math.cos(self.beta) 113 cos_gamma = math.cos(self.gamma) 114 115 ra = (self.b * self.c * sin_alpha) / V 116 rb = (self.a * self.c * sin_beta) / V 117 rc = (self.a * self.b * sin_gamma) / V 118 119 ralpha = math.acos( 120 (cos_beta * cos_gamma - cos_alpha) / (sin_beta * sin_gamma)) 121 rbeta = math.acos( 122 (cos_alpha * cos_gamma - cos_beta) / (sin_alpha * sin_gamma)) 123 rgamma = math.acos( 124 (cos_alpha * cos_beta - cos_gamma) / (sin_alpha * sin_beta)) 125 126 return UnitCell(ra, rb, rc, ralpha, rbeta, rgamma)
127
129 """Cartesian to fractional coordinates. 130 """ 131 sin_alpha = math.sin(self.alpha) 132 sin_beta = math.sin(self.beta) 133 sin_gamma = math.sin(self.gamma) 134 135 cos_alpha = math.cos(self.alpha) 136 cos_beta = math.cos(self.beta) 137 cos_gamma = math.cos(self.gamma) 138 139 v = self.calc_v() 140 141 f11 = self.a 142 f12 = self.b * cos_gamma 143 f13 = self.c * cos_beta 144 f22 = self.b * sin_gamma 145 f23 = (self.c * (cos_alpha - cos_beta * cos_gamma)) / (sin_gamma) 146 f33 = (self.c * v) / sin_gamma 147 148 orth_to_frac = numpy.array([ [f11, f12, f13], 149 [0.0, f22, f23], 150 [0.0, 0.0, f33] ], float) 151 152 return orth_to_frac
153
155 """Fractional to Cartesian coordinates. 156 """ 157 sin_alpha = math.sin(self.alpha) 158 sin_beta = math.sin(self.beta) 159 sin_gamma = math.sin(self.gamma) 160 161 cos_alpha = math.cos(self.alpha) 162 cos_beta = math.cos(self.beta) 163 cos_gamma = math.cos(self.gamma) 164 165 v = self.calc_v() 166 167 o11 = 1.0 / self.a 168 o12 = - cos_gamma / (self.a * sin_gamma) 169 o13 = (cos_gamma * cos_alpha - cos_beta) / (self.a * v * sin_gamma) 170 o22 = 1.0 / (self.b * sin_gamma) 171 o23 = (cos_gamma * cos_beta - cos_alpha) / (self.b * v * sin_gamma) 172 o33 = sin_gamma / (self.c * v) 173 174 frac_to_orth = numpy.array([ [o11, o12, o13], 175 [0.0, o22, o23], 176 [0.0, 0.0, o33] ], float) 177 178 return frac_to_orth
179
180 - def calc_orth_to_frac(self, v):
181 """Calculates and returns the fractional coordinate vector of 182 orthogonal vector v. 183 """ 184 return numpy.dot(self.orth_to_frac, v)
185
186 - def calc_frac_to_orth(self, v):
187 """Calculates and returns the orthogonal coordinate vector of 188 fractional vector v. 189 """ 190 return numpy.dot(self.frac_to_orth, v)
191
192 - def calc_orth_symop(self, symop):
193 """Calculates the orthogonal space symmetry operation (return SymOp) 194 given a fractional space symmetry operation (argument SymOp). 195 """ 196 RF = numpy.dot(symop.R, self.orth_to_frac) 197 ORF = numpy.dot(self.frac_to_orth, RF) 198 Ot = numpy.dot(self.frac_to_orth, symop.t) 199 return SpaceGroups.SymOp(ORF, Ot)
200
201 - def calc_orth_symop2(self, symop):
202 """Calculates the orthogonal space symmetry operation (return SymOp) 203 given a fractional space symmetry operation (argument SymOp). 204 """ 205 RF = numpy.dot(symop.R, self.orth_to_frac) 206 ORF = numpy.dot(self.frac_to_orth, RF) 207 Rt = numpy.dot(symop.R, symop.t) 208 ORt = numpy.dot(self.frac_to_orth, Rt) 209 210 return SpaceGroups.SymOp(ORF, ORt)
211
212 - def calc_cell(self, xyz):
213 """Returns the cell integer 3-Tuple where the xyz fractional 214 coordinates are located. 215 """ 216 if xyz[0] < 0.0: 217 cx = int(xyz[0] - 1.0) 218 else: 219 cx = int(xyz[0] + 1.0) 220 221 if xyz[1] < 0.0: 222 cy = int(xyz[1] - 1.0) 223 else: 224 cy = int(xyz[1] + 1.0) 225 226 if xyz[2] < 0.0: 227 cz = int(xyz[2] - 1.0) 228 else: 229 cz = int(xyz[2] + 1.0) 230 231 return (cx, cy, cz)
232
233 - def cell_search_iter(self):
234 """Yields 3-tuple integer translations over a 3x3x3 cube used by 235 other methods for searching nearby unit cells. 236 """ 237 cube = (-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0) 238 239 for i in cube: 240 for j in cube: 241 for k in cube: 242 yield i, j, k
243
244 - def iter_struct_orth_symops(self, struct):
245 """Iterate over the orthogonal-space symmetry operations which will 246 place a symmetry related structure near the argument struct. 247 """ 248 ## compute the centroid of the structure 249 n = 0 250 cent = numpy.zeros(3, float) 251 for atm in struct.iter_all_atoms(): 252 n += 1 253 cent += atm.position 254 centroid = cent / n 255 256 ccell = self.calc_cell(self.calc_orth_to_frac(centroid)) 257 centroid_cell = numpy.array(ccell, float) 258 259 ## compute the distance from the centroid to the farthest point from 260 ## it in the structure. 261 max_dist = 0.0 262 for frag in struct.iter_amino_acids(): 263 for atm in frag.iter_atoms(): 264 dist = AtomMath.length(atm.position - centroid) 265 max_dist = max(max_dist, dist) 266 max_dist2 = 2.0 * max_dist + 5.0 267 268 for symop in self.space_group.iter_symops(): 269 for i, j, k in self.cell_search_iter(): 270 271 cell_t = numpy.array([i, j, k], float) 272 symop_t = SpaceGroups.SymOp(symop.R, symop.t+cell_t) 273 274 xyz = self.calc_orth_to_frac(centroid) 275 xyz_symm = symop_t(xyz) 276 centroid2 = self.calc_frac_to_orth(xyz_symm) 277 278 if AtomMath.length(centroid - centroid2) <= max_dist2: 279 yield self.calc_orth_symop(symop_t)
280 281
282 -def strRT(R, T):
283 """Returns a string for a rotation/translation pair in a readable form. 284 """ 285 x = "[%6.3f %6.3f %6.3f %6.3f]\n" % ( 286 R[0,0], R[0,1], R[0,2], T[0]) 287 x += "[%6.3f %6.3f %6.3f %6.3f]\n" % ( 288 R[1,0], R[1,1], R[1,2], T[1]) 289 x += "[%6.3f %6.3f %6.3f %6.3f]\n" % ( 290 R[2,0], R[2,1], R[2,2], T[2]) 291 292 return x
293 294 295 ## <testing>
296 -def test_module():
297 print "=================================================" 298 print "TEST CASE #1: Triclinic unit cell" 299 print 300 301 uc = UnitCell(7.877, 7.210, 7.891, 105.563, 116.245, 79.836) 302 303 e = numpy.array([[1.0, 0.0, 0.0], 304 [0.0, 1.0, 0.0], 305 [0.0, 0.0, 1.0]], float) 306 307 308 print uc 309 print "volume = ", uc.calc_v() 310 print "cell volume = ", uc.calc_volume() 311 print "fractionalization matrix =\n", uc.calc_fractionalization_matrix() 312 print "orthogonalization matrix =\n", uc.calc_orthogonalization_matrix() 313 314 print "orth * e =\n", numpy.dot( 315 uc.calc_orthogonalization_matrix(), e) 316 317 318 print "calc_frac_to_orth" 319 vlist = [ 320 numpy.array([0.0, 0.0, 0.0]), 321 numpy.array([0.5, 0.5, 0.5]), 322 numpy.array([1.0, 1.0, 1.0]), 323 numpy.array([-0.13614, 0.15714, -0.07165]) ] 324 325 for v in vlist: 326 ov = uc.calc_frac_to_orth(v) 327 v2 = uc.calc_orth_to_frac(ov) 328 print "----" 329 print " ",v 330 print " ",ov 331 print " ",v2 332 print "----" 333 334 335 print "=================================================" 336 337 print 338 339 print "=================================================" 340 print "TEST CASE #2: Reciprocal of above unit cell " 341 print 342 343 ruc = uc.calc_reciprocal_unit_cell() 344 print ruc 345 print "volume = ", ruc.calc_v() 346 print "cell volume = ", ruc.calc_volume() 347 348 print "=================================================" 349 350 print 351 352 print "=================================================" 353 print "TEST CASE #3: Orthogonal space symmetry operations" 354 355 unitx = UnitCell(a = 64.950, 356 b = 64.950, 357 c = 68.670, 358 alpha = 90.00, 359 beta = 90.00, 360 gamma = 120.00, 361 space_group = "P 32 2 1") 362 print unitx 363 print 364 365 for symop in unitx.space_group.iter_symops(): 366 print "Fractional Space SymOp:" 367 print symop 368 print "Orthogonal Space SymOp:" 369 print unitx.calc_orth_symop(symop) 370 print
371 372 if __name__ == "__main__": 373 test_module() 374 ## </testing> 375