diffpy.structure 3.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. diffpy/Structure.py +35 -0
  2. diffpy/__init__.py +23 -0
  3. diffpy/structure/__init__.py +93 -0
  4. diffpy/structure/_legacy_importer.py +88 -0
  5. diffpy/structure/apps/__init__.py +17 -0
  6. diffpy/structure/apps/anyeye.py +284 -0
  7. diffpy/structure/apps/transtru.py +126 -0
  8. diffpy/structure/atom.py +544 -0
  9. diffpy/structure/expansion/__init__.py +27 -0
  10. diffpy/structure/expansion/makeellipsoid.py +129 -0
  11. diffpy/structure/expansion/shapeutils.py +44 -0
  12. diffpy/structure/expansion/supercell_mod.py +91 -0
  13. diffpy/structure/lattice.py +663 -0
  14. diffpy/structure/mmlibspacegroups.py +8154 -0
  15. diffpy/structure/parsers/__init__.py +83 -0
  16. diffpy/structure/parsers/p_auto.py +217 -0
  17. diffpy/structure/parsers/p_cif.py +876 -0
  18. diffpy/structure/parsers/p_discus.py +312 -0
  19. diffpy/structure/parsers/p_pdb.py +405 -0
  20. diffpy/structure/parsers/p_pdffit.py +290 -0
  21. diffpy/structure/parsers/p_rawxyz.py +149 -0
  22. diffpy/structure/parsers/p_xcfg.py +457 -0
  23. diffpy/structure/parsers/p_xyz.py +161 -0
  24. diffpy/structure/parsers/parser_index_mod.py +108 -0
  25. diffpy/structure/parsers/structureparser.py +80 -0
  26. diffpy/structure/pdffitstructure.py +109 -0
  27. diffpy/structure/sgtbxspacegroups.py +5198 -0
  28. diffpy/structure/spacegroupmod.py +329 -0
  29. diffpy/structure/spacegroups.py +1441 -0
  30. diffpy/structure/structure.py +866 -0
  31. diffpy/structure/structureerrors.py +35 -0
  32. diffpy/structure/symmetryutilities.py +1100 -0
  33. diffpy/structure/utils.py +126 -0
  34. diffpy/structure/version.py +26 -0
  35. diffpy.structure-3.2.0.dist-info/AUTHORS.rst +13 -0
  36. diffpy.structure-3.2.0.dist-info/LICENSE.rst +141 -0
  37. diffpy.structure-3.2.0.dist-info/LICENSE_DANSE.rst +50 -0
  38. diffpy.structure-3.2.0.dist-info/LICENSE_pymmlib.rst +203 -0
  39. diffpy.structure-3.2.0.dist-info/METADATA +197 -0
  40. diffpy.structure-3.2.0.dist-info/RECORD +42 -0
  41. diffpy.structure-3.2.0.dist-info/WHEEL +5 -0
  42. diffpy.structure-3.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,663 @@
1
+ #!/usr/bin/env python
2
+ ##############################################################################
3
+ #
4
+ # diffpy.structure by DANSE Diffraction group
5
+ # Simon J. L. Billinge
6
+ # (c) 2008 trustees of the Michigan State University.
7
+ # All rights reserved.
8
+ #
9
+ # File coded by: Pavol Juhas
10
+ #
11
+ # See AUTHORS.txt for a list of people who contributed.
12
+ # See LICENSE_DANSE.txt for license information.
13
+ #
14
+ ##############################################################################
15
+
16
+ """Class Lattice stores properties and provides simple operations in lattice
17
+ coordinate system.
18
+
19
+ Attributes
20
+ ----------
21
+ cartesian : Lattice
22
+ Constant instance of Lattice, default Cartesian system.
23
+ """
24
+
25
+ import math
26
+
27
+ import numpy
28
+ import numpy.linalg as numalg
29
+
30
+ from diffpy.structure.structureerrors import LatticeError
31
+
32
+ # Helper Functions -----------------------------------------------------------
33
+
34
+ # exact values of cosd
35
+ _EXACT_COSD = {0.0: +1.0, 60.0: +0.5, 90.0: 0.0, 120.0: -0.5, 180.0: -1.0, 240.0: -0.5, 270.0: 0.0, 300.0: +0.5}
36
+
37
+
38
+ def cosd(x):
39
+ """Return the cosine of *x* (measured in degrees).
40
+
41
+ Avoid round-off errors for exact cosine values.
42
+
43
+ Parameters
44
+ ----------
45
+ x : float
46
+ The angle in degrees.
47
+
48
+ Returns
49
+ -------
50
+ float
51
+ The cosine of the angle *x*.
52
+ """
53
+ rv = _EXACT_COSD.get(x % 360.0)
54
+ if rv is None:
55
+ rv = math.cos(math.radians(x))
56
+ return rv
57
+
58
+
59
+ def sind(x):
60
+ """Return the sine of *x* (measured in degrees).
61
+
62
+ Avoid round-off errors for exact sine values.
63
+
64
+ Parameters
65
+ ----------
66
+ x : float
67
+ The angle in degrees.
68
+
69
+ Returns
70
+ -------
71
+ float
72
+ The sine of the angle *x*.
73
+ """
74
+ return cosd(90.0 - x)
75
+
76
+
77
+ # ----------------------------------------------------------------------------
78
+
79
+
80
+ class Lattice(object):
81
+ """General coordinate system and associated operations.
82
+
83
+ Parameters
84
+ ----------
85
+ a : float or Lattice, Optional
86
+ The cell length *a*. When present, other cell parameters
87
+ must be also specified. When of the *Lattice* type, create
88
+ a duplicate Lattice.
89
+ b : float
90
+ The cell length *b*.
91
+ c : float
92
+ The cell length *c*.
93
+ alpha : float
94
+ The angle between the *b* and *c* axes in degrees.
95
+ beta : float
96
+ The angle between the *b* and *c* axes in degrees.
97
+ gamma : float
98
+ The angle between the *a* and *b* axes in degrees.
99
+ baserot : array_like, Optional
100
+ The 3x3 rotation matrix of the base vectors with respect
101
+ to their standard setting.
102
+ base : array_like, Optional
103
+ The 3x3 array of row base vectors. This must be the
104
+ only argument when present.
105
+
106
+ Attributes
107
+ ----------
108
+ metrics : numpy.ndarray
109
+ The metrics tensor.
110
+ base : numpy.ndarray
111
+ The 3x3 matrix of row base vectors in Cartesian coordinates,
112
+ which may be rotated, i.e., ``base = stdbase @ baserot``.
113
+ stdbase : numpy.ndarray
114
+ The 3x3 matrix of row base vectors in standard orientation.
115
+ baserot : numpy.ndarray
116
+ The rotation matrix for the `base`.
117
+ recbase : numpy.ndarray
118
+ The inverse of the `base` matrix, where the columns give
119
+ reciprocal vectors in Cartesian coordinates.
120
+ normbase : numpy.ndarray
121
+ The `base` vectors scaled by magnitudes of reciprocal cell lengths.
122
+ recnormbase : numpy.ndarray
123
+ The inverse of the `normbase` matrix.
124
+ isotropicunit : numpy.ndarray
125
+ The 3x3 tensor for a unit isotropic displacement parameters in this
126
+ coordinate system. This is an identity matrix when this Lattice
127
+ is orthonormal.
128
+
129
+ Note
130
+ ----
131
+ The array attributes are read-only. They get updated by changing
132
+ some lattice parameters or by calling the `setLatPar()` or
133
+ `setLatBase()` methods.
134
+
135
+ Examples
136
+ --------
137
+ Create a Cartesian coordinate system:
138
+
139
+ >>> Lattice()
140
+
141
+ Create coordinate system with the cell lengths `a`, `b`, `c`
142
+ and cell angles `alpha`, `beta`, `gamma` in degrees:
143
+
144
+ >>> Lattice(a, b, c, alpha, beta, gamma)
145
+
146
+ Create a duplicate of an existing Lattice `lat`:
147
+
148
+ >>> Lattice(lat)
149
+
150
+ Create coordinate system with the base vectors given by rows
151
+ of the `abc` matrix:
152
+
153
+ >>> Lattice(base=abc)
154
+ """
155
+
156
+ # round-off tolerance
157
+ _epsilon = 1.0e-8
158
+
159
+ # properties -------------------------------------------------------------
160
+
161
+ a = property(
162
+ lambda self: self._a, lambda self, value: self.setLatPar(a=value), doc="The unit cell length *a*."
163
+ )
164
+
165
+ b = property(
166
+ lambda self: self._b, lambda self, value: self.setLatPar(b=value), doc="The unit cell length *b*."
167
+ )
168
+
169
+ c = property(
170
+ lambda self: self._c, lambda self, value: self.setLatPar(c=value), doc="The unit cell length *c*."
171
+ )
172
+
173
+ alpha = property(
174
+ lambda self: self._alpha,
175
+ lambda self, value: self.setLatPar(alpha=value),
176
+ doc="The cell angle *alpha* in degrees.",
177
+ )
178
+
179
+ beta = property(
180
+ lambda self: self._beta,
181
+ lambda self, value: self.setLatPar(beta=value),
182
+ doc="The cell angle *beta* in degrees.",
183
+ )
184
+
185
+ gamma = property(
186
+ lambda self: self._gamma,
187
+ lambda self, value: self.setLatPar(gamma=value),
188
+ doc="The cell angle *gamma* in degrees.",
189
+ )
190
+
191
+ # read-only derived properties
192
+
193
+ @property
194
+ def unitvolume(self):
195
+ """The unit cell volume when `a = b = c = 1`."""
196
+ # Recalculate lattice cosines to ensure this is right
197
+ # even if ca, cb, cg data were not yet updated.
198
+ ca = cosd(self.alpha)
199
+ cb = cosd(self.beta)
200
+ cg = cosd(self.gamma)
201
+ rv = math.sqrt(1.0 + 2.0 * ca * cb * cg - ca * ca - cb * cb - cg * cg)
202
+ return rv
203
+
204
+ volume = property(lambda self: self.a * self.b * self.c * self.unitvolume, doc="The unit cell volume.")
205
+
206
+ ar = property(lambda self: self._ar, doc="The cell length *a* of the reciprocal lattice.")
207
+
208
+ br = property(lambda self: self._br, doc="The cell length *b* of the reciprocal lattice.")
209
+
210
+ cr = property(lambda self: self._cr, doc="The cell length *c* of the reciprocal lattice.")
211
+
212
+ alphar = property(lambda self: self._alphar, doc="The reciprocal cell angle *alpha* in degrees.")
213
+
214
+ betar = property(lambda self: self._betar, doc="The reciprocal cell angle *beta* in degrees")
215
+
216
+ gammar = property(lambda self: self._gammar, doc="The reciprocal cell angle *gamma* in degrees")
217
+
218
+ ca = property(lambda self: self._ca, doc="The cosine of the cell angle *alpha*.")
219
+
220
+ cb = property(lambda self: self._cb, doc="The cosine of the cell angle *beta*.")
221
+
222
+ cg = property(lambda self: self._cg, doc="The cosine of the cell angle *gamma*.")
223
+
224
+ sa = property(lambda self: self._sa, doc="The sine of the cell angle *alpha*.")
225
+
226
+ sb = property(lambda self: self._sb, doc="The sine of the cell angle *beta*.")
227
+
228
+ sg = property(lambda self: self._sg, doc="The sine of the cell angle *gamma*.")
229
+
230
+ car = property(lambda self: self._car, doc="The cosine of the reciprocal angle *alpha*.")
231
+
232
+ cbr = property(lambda self: self._cbr, doc="The cosine of the reciprocal angle *beta*.")
233
+
234
+ cgr = property(lambda self: self._cgr, doc="The cosine of the reciprocal angle *gamma*.")
235
+
236
+ sar = property(lambda self: self._sar, doc="The sine of the reciprocal angle *alpha*.")
237
+
238
+ sbr = property(lambda self: self._sbr, doc="The sine of the reciprocal angle *beta*.")
239
+
240
+ sgr = property(lambda self: self._sgr, doc="The sine of the reciprocal angle *gamma*.")
241
+
242
+ # done with properties ---------------------------------------------------
243
+
244
+ def __init__(self, a=None, b=None, c=None, alpha=None, beta=None, gamma=None, baserot=None, base=None):
245
+ # build a set of provided argument names for later use.
246
+ apairs = (
247
+ ("a", a),
248
+ ("b", b),
249
+ ("c", c),
250
+ ("alpha", alpha),
251
+ ("beta", beta),
252
+ ("gamma", gamma),
253
+ ("baserot", baserot),
254
+ ("base", base),
255
+ )
256
+ argset = set(n for n, v in apairs if v is not None)
257
+ # initialize data members, they values will be set by setLatPar()
258
+ self._a = self._b = self._c = None
259
+ self._alpha = self._beta = self._gamma = None
260
+ self._ca = self._cb = self._cg = None
261
+ self._sa = self._sb = self._sg = None
262
+ self._ar = self._br = self._cr = None
263
+ self._alphar = self._betar = self._gammar = None
264
+ self._car = self._cbr = self._cgr = None
265
+ self._sar = self._sbr = self._sgr = None
266
+ self.baserot = numpy.identity(3)
267
+ self.base = self.recbase = None
268
+ self.normbase = self.recnormbase = None
269
+ # work out argument variants
270
+ # Lattice()
271
+ if not argset:
272
+ self.setLatPar(1.0, 1.0, 1.0, 90.0, 90.0, 90.0, baserot)
273
+ # Lattice(base=abc)
274
+ elif base is not None:
275
+ if len(argset) > 1:
276
+ raise ValueError("'base' must be the only argument.")
277
+ self.setLatBase(base)
278
+ # Lattice(lat)
279
+ elif isinstance(a, Lattice):
280
+ if len(argset) > 1:
281
+ raise ValueError("Lattice object must be the only argument.")
282
+ self.__dict__.update(a.__dict__)
283
+ # otherwise do default Lattice(a, b, c, alpha, beta, gamma)
284
+ else:
285
+ abcabg = ("a", "b", "c", "alpha", "beta", "gamma")
286
+ if not argset.issuperset(abcabg):
287
+ raise ValueError("Provide all 6 cell parameters.")
288
+ self.setLatPar(a, b, c, alpha, beta, gamma, baserot=baserot)
289
+ return
290
+
291
+ def setLatPar(self, a=None, b=None, c=None, alpha=None, beta=None, gamma=None, baserot=None):
292
+ """Set one or more lattice parameters.
293
+
294
+ This updates all attributes that depend on the lattice parameters.
295
+
296
+ Parameters
297
+ ----------
298
+ a : float, Optional
299
+ The new value of the cell length *a*.
300
+ b : float, Optional
301
+ The new value of the cell length *b*.
302
+ c : float, Optional
303
+ The new value of the cell length *c*.
304
+ alpha : float, Optional
305
+ The new value of the cell angle *alpha* in degrees.
306
+ beta : float, Optional
307
+ The new value of the cell angle *beta* in degrees.
308
+ gamma : float, Optional
309
+ The new value of the cell angle *gamma* in degrees.
310
+ baserot : array_like, Optional
311
+ The new 3x3 rotation matrix of the base vectors with respect
312
+ to their standard setting in Cartesian coordinates.
313
+
314
+ Note
315
+ ----
316
+ Parameters that are not specified will keep their initial
317
+ values.
318
+ """
319
+ if a is not None:
320
+ self._a = float(a)
321
+ if b is not None:
322
+ self._b = float(b)
323
+ if c is not None:
324
+ self._c = float(c)
325
+ if alpha is not None:
326
+ self._alpha = float(alpha)
327
+ if beta is not None:
328
+ self._beta = float(beta)
329
+ if gamma is not None:
330
+ self._gamma = float(gamma)
331
+ if baserot is not None:
332
+ self.baserot = numpy.array(baserot)
333
+ self._ca = ca = cosd(self.alpha)
334
+ self._cb = cb = cosd(self.beta)
335
+ self._cg = cg = cosd(self.gamma)
336
+ self._sa = sa = sind(self.alpha)
337
+ self._sb = sb = sind(self.beta)
338
+ self._sg = sg = sind(self.gamma)
339
+ # cache the unit volume value
340
+ Vunit = self.unitvolume
341
+ # reciprocal lattice
342
+ self._ar = ar = sa / (self.a * Vunit)
343
+ self._br = br = sb / (self.b * Vunit)
344
+ self._cr = cr = sg / (self.c * Vunit)
345
+ self._car = car = (cb * cg - ca) / (sb * sg)
346
+ self._cbr = cbr = (ca * cg - cb) / (sa * sg)
347
+ self._cgr = cgr = (ca * cb - cg) / (sa * sb)
348
+ self._sar = math.sqrt(1.0 - car * car)
349
+ self._sbr = math.sqrt(1.0 - cbr * cbr)
350
+ self._sgr = sgr = math.sqrt(1.0 - cgr * cgr)
351
+ self._alphar = math.degrees(math.acos(car))
352
+ self._betar = math.degrees(math.acos(cbr))
353
+ self._gammar = math.degrees(math.acos(cgr))
354
+ # metrics tensor
355
+ self.metrics = numpy.array(
356
+ [
357
+ [self.a * self.a, self.a * self.b * cg, self.a * self.c * cb],
358
+ [self.b * self.a * cg, self.b * self.b, self.b * self.c * ca],
359
+ [self.c * self.a * cb, self.c * self.b * ca, self.c * self.c],
360
+ ],
361
+ dtype=float,
362
+ )
363
+ # standard Cartesian coordinates of lattice vectors
364
+ self.stdbase = numpy.array(
365
+ [[1.0 / ar, -cgr / sgr / ar, cb * self.a], [0.0, self.b * sa, self.b * ca], [0.0, 0.0, self.c]],
366
+ dtype=float,
367
+ )
368
+ # Cartesian coordinates of lattice vectors
369
+ self.base = numpy.dot(self.stdbase, self.baserot)
370
+ self.recbase = numalg.inv(self.base)
371
+ # bases normalized to unit reciprocal vectors
372
+ self.normbase = self.base * [[ar], [br], [cr]]
373
+ self.recnormbase = self.recbase / [ar, br, cr]
374
+ self.isotropicunit = _isotropicunit(self.recnormbase)
375
+ return
376
+
377
+ def setLatBase(self, base):
378
+ """Set new base vectors for this lattice.
379
+
380
+ This updates the cell lengths and cell angles according to the
381
+ new base. The `stdbase`, `baserot`, and `metrics` attributes
382
+ are also updated.
383
+
384
+ Parameters
385
+ ----------
386
+ base : array_like
387
+ The 3x3 matrix of row base vectors expressed
388
+ in Cartesian coordinates.
389
+ """
390
+ self.base = numpy.array(base)
391
+ detbase = numalg.det(self.base)
392
+ if abs(detbase) < 1.0e-8:
393
+ emsg = "base vectors are degenerate"
394
+ raise LatticeError(emsg)
395
+ elif detbase < 0.0:
396
+ emsg = "base is not right-handed"
397
+ raise LatticeError(emsg)
398
+ self._a = a = math.sqrt(numpy.dot(self.base[0, :], self.base[0, :]))
399
+ self._b = b = math.sqrt(numpy.dot(self.base[1, :], self.base[1, :]))
400
+ self._c = c = math.sqrt(numpy.dot(self.base[2, :], self.base[2, :]))
401
+ self._ca = ca = numpy.dot(self.base[1, :], self.base[2, :]) / (b * c)
402
+ self._cb = cb = numpy.dot(self.base[0, :], self.base[2, :]) / (a * c)
403
+ self._cg = cg = numpy.dot(self.base[0, :], self.base[1, :]) / (a * b)
404
+ self._sa = sa = math.sqrt(1.0 - ca**2)
405
+ self._sb = sb = math.sqrt(1.0 - cb**2)
406
+ self._sg = sg = math.sqrt(1.0 - cg**2)
407
+ self._alpha = math.degrees(math.acos(ca))
408
+ self._beta = math.degrees(math.acos(cb))
409
+ self._gamma = math.degrees(math.acos(cg))
410
+ # cache the unit volume value
411
+ Vunit = self.unitvolume
412
+ # reciprocal lattice
413
+ self._ar = ar = sa / (self.a * Vunit)
414
+ self._br = br = sb / (self.b * Vunit)
415
+ self._cr = cr = sg / (self.c * Vunit)
416
+ self._car = car = (cb * cg - ca) / (sb * sg)
417
+ self._cbr = cbr = (ca * cg - cb) / (sa * sg)
418
+ self._cgr = cgr = (ca * cb - cg) / (sa * sb)
419
+ self._sar = math.sqrt(1.0 - car**2)
420
+ self._sbr = math.sqrt(1.0 - cbr**2)
421
+ self._sgr = sgr = math.sqrt(1.0 - cgr**2)
422
+ self._alphar = math.degrees(math.acos(car))
423
+ self._betar = math.degrees(math.acos(cbr))
424
+ self._gammar = math.degrees(math.acos(cgr))
425
+ # standard orientation of lattice vectors
426
+ self.stdbase = numpy.array(
427
+ [[1.0 / ar, -cgr / sgr / ar, cb * a], [0.0, b * sa, b * ca], [0.0, 0.0, c]], dtype=float
428
+ )
429
+ # calculate unit cell rotation matrix, base = stdbase @ baserot
430
+ self.baserot = numpy.dot(numalg.inv(self.stdbase), self.base)
431
+ self.recbase = numalg.inv(self.base)
432
+ # bases normalized to unit reciprocal vectors
433
+ self.normbase = self.base * [[ar], [br], [cr]]
434
+ self.recnormbase = self.recbase / [ar, br, cr]
435
+ self.isotropicunit = _isotropicunit(self.recnormbase)
436
+ # update metrics tensor
437
+ self.metrics = numpy.array(
438
+ [[a * a, a * b * cg, a * c * cb], [b * a * cg, b * b, b * c * ca], [c * a * cb, c * b * ca, c * c]],
439
+ dtype=float,
440
+ )
441
+ return
442
+
443
+ def abcABG(self):
444
+ """Return the cell parameters in the standard setting.
445
+ Returns
446
+ -------
447
+ tuple :
448
+ A tuple of ``(a, b, c, alpha, beta, gamma)``.
449
+ """
450
+ rv = (self.a, self.b, self.c, self.alpha, self.beta, self.gamma)
451
+ return rv
452
+
453
+ def reciprocal(self):
454
+ """Return the reciprocal lattice of the current lattice.
455
+ Returns
456
+ -------
457
+ Lattice
458
+ The reciprocal lattice of the current lattice.
459
+ """
460
+ rv = Lattice(base=numpy.transpose(self.recbase))
461
+ return rv
462
+
463
+ def cartesian(self, u):
464
+ """Transform lattice vector to Cartesian coordinates.
465
+
466
+ Parameters
467
+ ----------
468
+ u : array_like
469
+ Vector of lattice coordinates or an Nx3 array
470
+ of lattice vectors.
471
+
472
+ Returns
473
+ -------
474
+ rc : numpy.ndarray
475
+ Cartesian coordinates of the *u* vector.
476
+ """
477
+ rc = numpy.dot(u, self.base)
478
+ return rc
479
+
480
+ def fractional(self, rc):
481
+ """Transform Cartesian vector to fractional lattice coordinates.
482
+
483
+ Parameters
484
+ ----------
485
+ rc : array_like
486
+ A vector of Cartesian coordinates or an Nx3 array of
487
+ Cartesian vectors.
488
+
489
+ Returns
490
+ -------
491
+ u : numpy.ndarray
492
+ Fractional coordinates of the Cartesian vector *rc*.
493
+ """
494
+ u = numpy.dot(rc, self.recbase)
495
+ return u
496
+
497
+ def dot(self, u, v):
498
+ """Calculate dot product of 2 lattice vectors.
499
+
500
+ Parameters
501
+ ----------
502
+ u : array_like
503
+ The first lattice vector or an Nx3 array.
504
+ v : array_like
505
+ The second lattice vector or an array of
506
+ the same shape as *u*.
507
+
508
+ Returns
509
+ -------
510
+ float or numpy.ndarray
511
+ The dot product of lattice vectors *u*, *v*.
512
+ """
513
+ dp = (u * numpy.dot(v, self.metrics)).sum(axis=-1)
514
+ return dp
515
+
516
+ def norm(self, xyz):
517
+ """Calculate norm of a lattice vector.
518
+
519
+ Parameters
520
+ ----------
521
+ xyz : array_like
522
+ A vector or an Nx3 array of fractional coordinates.
523
+
524
+ Returns
525
+ -------
526
+ float or numpy.ndarray
527
+ The magnitude of the lattice vector *xyz*.
528
+ """
529
+ # this is a few percent faster than sqrt(dot(u, u)).
530
+ return numpy.sqrt((self.cartesian(xyz) ** 2).sum(axis=-1))
531
+
532
+ def rnorm(self, hkl):
533
+ """Calculate norm of a reciprocal vector.
534
+
535
+ Parameters
536
+ ----------
537
+ hkl : array_like
538
+ A vector or an Nx3 array of reciprocal coordinates.
539
+
540
+ Returns
541
+ -------
542
+ float or numpy.ndarray
543
+ The magnitude of the reciprocal vector *hkl*.
544
+ """
545
+ hklcartn = numpy.dot(hkl, self.recbase.T)
546
+ return numpy.sqrt((hklcartn**2).sum(axis=-1))
547
+
548
+ def dist(self, u, v):
549
+ """Calculate distance between 2 points in lattice coordinates.
550
+
551
+ Parameters
552
+ ----------
553
+ u : array_like
554
+ A vector or an Nx3 matrix of fractional coordinates.
555
+ v : numpy.ndarray
556
+ A vector or an Nx3 matrix of fractional coordinates.
557
+
558
+ Note
559
+ ----
560
+ *u* and *v* must be of the same shape when matrices.
561
+
562
+ Returns
563
+ -------
564
+ float or numpy.ndarray
565
+ The distance between lattice points *u* and *v*.
566
+ """
567
+ duv = numpy.asarray(u) - v
568
+ return self.norm(duv)
569
+
570
+ def angle(self, u, v):
571
+ """Calculate angle between 2 lattice vectors in degrees.
572
+
573
+ Parameters
574
+ ----------
575
+ u : array_like
576
+ The first lattice vector.
577
+ v : array_like
578
+ The second lattice vector.
579
+
580
+ Returns
581
+ -------
582
+ float
583
+ The angle between lattice vectors *u* and *v* in degrees.
584
+ """
585
+ ca = self.dot(u, v) / (self.norm(u) * self.norm(v))
586
+ # avoid round-off errors that would make abs(ca) greater than 1
587
+ if numpy.isscalar(ca):
588
+ ca = max(min(ca, 1), -1)
589
+ rv = math.degrees(math.acos(ca))
590
+ else:
591
+ ca[ca < -1] = -1
592
+ ca[ca > +1] = +1
593
+ rv = numpy.degrees(numpy.arccos(ca))
594
+ return rv
595
+
596
+ def isanisotropic(self, umx):
597
+ """True if displacement parameter matrix is anisotropic.
598
+
599
+ This checks if the specified matrix of anisotropic displacement
600
+ parameters (ADP) differs from isotropic values for this lattice
601
+ by more than a small round-off error.
602
+
603
+ Parameters
604
+ ----------
605
+ umx : array_like
606
+ The 3x3 matrix of displacement parameters.
607
+
608
+ Returns
609
+ -------
610
+ bool
611
+ True when *umx* is anisotropic by more than a round-off error.
612
+ """
613
+ umx = numpy.asarray(umx)
614
+ utr = numpy.trace(umx) / umx.shape[0]
615
+ udmax = numpy.fabs(umx - utr * self.isotropicunit).max()
616
+ rv = udmax > self._epsilon
617
+ return rv
618
+
619
+ def __repr__(self):
620
+ """String representation of this lattice."""
621
+ I3 = numpy.identity(3, dtype=float)
622
+ rotbaseI3diff = max(numpy.reshape(numpy.fabs(self.baserot - I3), 9))
623
+ cartlatpar = numpy.array([1.0, 1.0, 1.0, 90.0, 90.0, 90.0])
624
+ latpardiff = cartlatpar - self.abcABG()
625
+ if rotbaseI3diff > self._epsilon:
626
+ s = "Lattice(base=%r)" % self.base
627
+ elif numpy.fabs(latpardiff).max() < self._epsilon:
628
+ s = "Lattice()"
629
+ else:
630
+ s = "Lattice(a=%g, b=%g, c=%g, alpha=%g, beta=%g, gamma=%g)" % self.abcABG()
631
+ return s
632
+
633
+
634
+ # End of class Lattice
635
+
636
+ # Local Helpers --------------------------------------------------------------
637
+
638
+
639
+ def _isotropicunit(recnormbase):
640
+ """Calculate tensor of unit isotropic displacement parameters.
641
+
642
+ Parameters
643
+ ----------
644
+ recnormbase : numpy.ndarray
645
+ The inverse of normalized base vectors of some lattice.
646
+
647
+ Returns
648
+ -------
649
+ numpy.ndarray
650
+ The 3x3 matrix of displacement parameters corresponding to
651
+ a unit isotropic displacements.
652
+ """
653
+ isounit = numpy.dot(recnormbase.T, recnormbase)
654
+ # ensure there are no round-off deviations on the diagonal
655
+ isounit[0, 0] = 1
656
+ isounit[1, 1] = 1
657
+ isounit[2, 2] = 1
658
+ return isounit
659
+
660
+
661
+ # Module Constants -----------------------------------------------------------
662
+
663
+ cartesian = Lattice()