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,544 @@
1
+ #!/usr/bin/env python
2
+ ##############################################################################
3
+ #
4
+ # diffpy.structure by DANSE Diffraction group
5
+ # Simon J. L. Billinge
6
+ # (c) 2006 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
+ """
17
+ Provide class Atom for managing properties of an atom in structure model.
18
+ """
19
+
20
+ import numpy
21
+
22
+ from diffpy.structure.lattice import cartesian as cartesian_lattice
23
+
24
+ # conversion constants
25
+ _BtoU = 1.0 / (8 * numpy.pi**2)
26
+ _UtoB = 1.0 / _BtoU
27
+
28
+ # ----------------------------------------------------------------------------
29
+
30
+
31
+ class Atom(object):
32
+ """Storage of structure information relevant for a single atom.
33
+
34
+ This class manages atom information such as element symbol, position
35
+ in fractional and Cartesian coordinates, atomic displacement parameters
36
+ and so forth.
37
+
38
+ Parameters
39
+ ----------
40
+ atype : str or Atom, Optional
41
+ The string atom type to be set as the `element` attribute.
42
+ By default an empty string. When of the `Atom` type, create
43
+ a copy of `atype` and adjust it per other arguments.
44
+ xyz : numpy.ndarray, Optional
45
+ Fractional coordinates within the associated `lattice`.
46
+ By default ``[0, 0, 0]``.
47
+ label : str, Optional
48
+ A unique string `label` for referring to this `Atom`.
49
+ By default an empty string.
50
+ occupancy : float, Optional
51
+ The initial `occupancy` of this atom, by default ``1``.
52
+ anisotropy : bool, Optional
53
+ The flag for anisotropic thermal displacements parameters.
54
+ This overrides `anisotropy` implied by presence of the
55
+ *U* or *Uisoequiv* arguments. Defaults to ``False``
56
+ when not set in any other way.
57
+ U : numpy.ndarray, Optional
58
+ The 3x3 matrix of anisotropic thermal displacement parameters.
59
+ When present `anisotropy` defaults to ``True``.
60
+ Uisoequiv: float, Optional
61
+ The isotropic atomic displacement parameter. The `anisotropy`
62
+ defaults to ``False`` when present. Only one of the *U* and
63
+ *Uisoequiv* arguments may be provided at the same time. Assume
64
+ zero atomic displacements when *U* and *Uisoequiv* are unset.
65
+ lattice : Lattice, Optional
66
+ Coordinate system for the fractional coordinates `xyz`.
67
+ Use the absolute Cartesian system when ``None``.
68
+
69
+ Attributes
70
+ ----------
71
+ element : str
72
+ The string type of the atom. An element or ion symbol.
73
+ xyz : numpy.ndarray
74
+ The fractional coordinates in the associated `lattice`.
75
+ label : str
76
+ A unique string label referring to this atom, for example, "C_1".
77
+ The *label* can be used to reference this atom when contained in
78
+ a `Structure` object.
79
+ occupancy : float
80
+ The fractional occupancy of this atom.
81
+ lattice : Lattice
82
+ Coordinate system for the fractional coordinates `xyz` and
83
+ the tensor of atomic displacement parameters `U`.
84
+ Use the absolute Cartesian coordinates when ``None``.
85
+
86
+ Note
87
+ ----
88
+ Cannot use both U and Uisoequiv arguments at the same time.
89
+ """
90
+
91
+ # Private attributes
92
+ #
93
+ # _U : 3-by-3 ndarray
94
+ # Internal storage of the displacement parameters.
95
+
96
+ # instance attributes that have immutable default values
97
+ element = ""
98
+ """str: Default values of `element`."""
99
+
100
+ label = ""
101
+ """str: Default values of `label`."""
102
+
103
+ occupancy = 1.0
104
+ """float: Default values of `occupancy`."""
105
+
106
+ _anisotropy = False
107
+ lattice = None
108
+ """None: Default values of `lattice`."""
109
+
110
+ def __init__(
111
+ self,
112
+ atype=None,
113
+ xyz=None,
114
+ label=None,
115
+ occupancy=None,
116
+ anisotropy=None,
117
+ U=None,
118
+ Uisoequiv=None,
119
+ lattice=None,
120
+ ):
121
+ # check arguments
122
+ if U is not None and Uisoequiv is not None:
123
+ emsg = "Cannot use both U and Uisoequiv arguments."
124
+ raise ValueError(emsg)
125
+ # declare data members
126
+ self.xyz = numpy.zeros(3, dtype=float)
127
+ self._U = numpy.zeros((3, 3), dtype=float)
128
+ # assign them as needed
129
+ if isinstance(atype, Atom):
130
+ atype.__copy__(target=self)
131
+ elif atype is not None:
132
+ self.element = atype
133
+ # take care of remaining arguments
134
+ if xyz is not None:
135
+ self.xyz[:] = xyz
136
+ if label is not None:
137
+ self.label = label
138
+ if occupancy is not None:
139
+ self.occupancy = float(occupancy)
140
+ if U is not None:
141
+ self.anisotropy = True
142
+ self._U[:] = U
143
+ if Uisoequiv is not None:
144
+ self.anisotropy = False
145
+ self.Uisoequiv = Uisoequiv
146
+ # lattice needs to be set before anisotropy
147
+ if lattice is not None:
148
+ self.lattice = lattice
149
+ # process anisotropy after U, Uisoequiv and lattice.
150
+ if anisotropy is not None:
151
+ self.anisotropy = bool(anisotropy)
152
+ return
153
+
154
+ def msdLat(self, vl):
155
+ """Calculate mean square displacement along the lattice vector.
156
+
157
+ Parameters
158
+ ----------
159
+ vl : array_like
160
+ The vector in lattice coordinates.
161
+
162
+ Returns
163
+ -------
164
+ float
165
+ The mean square displacement along *vl*.
166
+ """
167
+ if not self.anisotropy:
168
+ return self.Uisoequiv
169
+ # here we need to calculate msd
170
+ lat = self.lattice or cartesian_lattice
171
+ vln = numpy.array(vl, dtype=float) / lat.norm(vl)
172
+ G = lat.metrics
173
+ rhs = numpy.array([G[0] * lat.ar, G[1] * lat.br, G[2] * lat.cr], dtype=float)
174
+ rhs = numpy.dot(rhs, vln)
175
+ msd = numpy.dot(rhs, numpy.dot(self.U, rhs))
176
+ return msd
177
+
178
+ def msdCart(self, vc):
179
+ """Calculate mean square displacement along the Cartesian vector.
180
+
181
+ Parameters
182
+ ----------
183
+ vc : array_like
184
+ Vector in Cartesian coordinates.
185
+
186
+ Returns
187
+ -------
188
+ float
189
+ The mean square displacement along *vc*.
190
+ """
191
+ if not self.anisotropy:
192
+ return self.Uisoequiv
193
+ # here we need to calculate msd
194
+ lat = self.lattice or cartesian_lattice
195
+ vcn = numpy.array(vc, dtype=float)
196
+ vcn /= numpy.sqrt(numpy.sum(vcn**2))
197
+ F1 = lat.normbase
198
+ Uc = numpy.dot(numpy.transpose(F1), numpy.dot(self._U, F1))
199
+ msd = numpy.dot(vcn, numpy.dot(Uc, vcn))
200
+ return msd
201
+
202
+ def __repr__(self):
203
+ """String representation of this Atom."""
204
+ xyz = self.xyz
205
+ s = "%-4s %8.6f %8.6f %8.6f %6.4f" % (self.element, xyz[0], xyz[1], xyz[2], self.occupancy)
206
+ return s
207
+
208
+ def __copy__(self, target=None):
209
+ """Create a copy of this instance.
210
+
211
+ Parameters
212
+ ----------
213
+ target : Atom, Optional
214
+ An already existing `Atom` object to be updated to a duplicate
215
+ of this `Atom`. Create a new Atom object when not specified.
216
+ This facilitates extension of the `__copy__` method
217
+ in a derived class.
218
+
219
+ Returns
220
+ -------
221
+ Atom
222
+ The copy of this object.
223
+ """
224
+ if target is None:
225
+ target = Atom()
226
+ elif target is self:
227
+ return target
228
+ target.__dict__.update(self.__dict__)
229
+ target.xyz = numpy.copy(self.xyz)
230
+ target._U = numpy.copy(self._U)
231
+ return target
232
+
233
+ # property handlers ------------------------------------------------------
234
+
235
+ x = property(
236
+ lambda self: self.xyz[0],
237
+ lambda self, val: self.xyz.__setitem__(0, val),
238
+ doc="float : fractional coordinate *x*, same as ``xyz[0]``.",
239
+ )
240
+ y = property(
241
+ lambda self: self.xyz[1],
242
+ lambda self, val: self.xyz.__setitem__(1, val),
243
+ doc="float : fractional coordinate *y*, same as ``xyz[1]``.",
244
+ )
245
+ z = property(
246
+ lambda self: self.xyz[2],
247
+ lambda self, val: self.xyz.__setitem__(2, val),
248
+ doc="float : fractional coordinate *z*, same as ``xyz[2]``.",
249
+ )
250
+
251
+ # xyz_cartn
252
+
253
+ @property
254
+ def xyz_cartn(self):
255
+ """numpy.ndarray: Atom position in absolute Cartesian coordinates.
256
+
257
+ This is computed from fractional coordinates `xyz` and the
258
+ current `lattice` setup. Assignment to *xyz_cartn* or
259
+ its components is applied on fractional coordinates `xyz`.
260
+ """
261
+ if not self.lattice:
262
+ rv = self.xyz
263
+ else:
264
+ rv = _AtomCartesianCoordinates(self)
265
+ return rv
266
+
267
+ @xyz_cartn.setter
268
+ def xyz_cartn(self, value):
269
+ if not self.lattice:
270
+ self.xyz[:] = value
271
+ else:
272
+ self.xyz[:] = self.lattice.fractional(value)
273
+ return
274
+
275
+ # anisotropy
276
+
277
+ @property
278
+ def anisotropy(self):
279
+ """bool : Flag for allowing anisotropic displacement parameters.
280
+
281
+ When ``False`` the tensor of thermal displacement parameters `U`
282
+ must be isotropic and only its diagonal elements are taken into
283
+ account.
284
+ """
285
+ return self._anisotropy
286
+
287
+ @anisotropy.setter
288
+ def anisotropy(self, value):
289
+ if bool(value) is self._anisotropy:
290
+ return
291
+ # convert from isotropic to anisotropic
292
+ if value:
293
+ self._U = self.U
294
+ # otherwise convert from anisotropic to isotropic
295
+ else:
296
+ self._U[0, 0] = self.Uisoequiv
297
+ self._anisotropy = bool(value)
298
+ return
299
+
300
+ # U
301
+
302
+ @property
303
+ def U(self):
304
+ """numpy.ndarray : The 3x3 matrix of anisotropic atomic displacements.
305
+
306
+ For isotropic displacements (when `anisotropy` is ``False``)
307
+ assignment to *U* uses only the first ``Unew[0, 0]`` element
308
+ and the remaining components of *U* are adjusted to obtain
309
+ isotropic tensor in the active `lattice`.
310
+
311
+ Note
312
+ ----
313
+ Elements of the *U* tensor such as ``U[0, 1]`` should be
314
+ considered read-only as setting them directly leads to
315
+ undefined behavior. Use the `U11`, `U22`, ..., or `B11`,
316
+ `B22`, ..., descriptors to set only some *U* components.
317
+ """
318
+ if not self.anisotropy:
319
+ # for isotropic displacements assume first element
320
+ # to be equal to the displacement value
321
+ lat = self.lattice or cartesian_lattice
322
+ numpy.multiply(self._U[0, 0], lat.isotropicunit, out=self._U)
323
+ return self._U
324
+
325
+ @U.setter
326
+ def U(self, value):
327
+ self._U[:] = value
328
+ return
329
+
330
+ # Uij elements
331
+
332
+ def _get_Uij(self, i, j):
333
+ """The getter function for the `U11`, `U22`, ..., properties."""
334
+ if self.anisotropy:
335
+ return self._U[i, j]
336
+ lat = self.lattice or cartesian_lattice
337
+ return self._U[0, 0] * lat.isotropicunit[i, j]
338
+
339
+ def _set_Uij(self, i, j, value):
340
+ """The setter function for the `U11`, `U22`, ..., properties."""
341
+ self._U[i, j] = value
342
+ self._U[j, i] = value
343
+ if not self._anisotropy and i == j != 0:
344
+ self._U[0, 0] = value
345
+ return
346
+
347
+ # _doc_uii, _doc_uij are temporary local variables.
348
+
349
+ _doc_uii = """
350
+ float : The ``U[{0}, {0}]`` component of the displacement tensor `U`.
351
+
352
+ When `anisotropy` is ``False`` setting a new value updates entire
353
+ tensor *U*.
354
+ """
355
+
356
+ U11 = property(
357
+ lambda self: self._get_Uij(0, 0), lambda self, value: self._set_Uij(0, 0, value), doc=_doc_uii.format(0)
358
+ )
359
+ U22 = property(
360
+ lambda self: self._get_Uij(1, 1), lambda self, value: self._set_Uij(1, 1, value), doc=_doc_uii.format(1)
361
+ )
362
+ U33 = property(
363
+ lambda self: self._get_Uij(2, 2), lambda self, value: self._set_Uij(2, 2, value), doc=_doc_uii.format(2)
364
+ )
365
+
366
+ _doc_uij = """
367
+ float : The ``U[{0}, {1}]`` element of the displacement tensor `U`.
368
+
369
+ Sets ``U[{1}, {0}]`` together with ``U[{0}, {1}]``. Assignment
370
+ has no effect when `anisotropy` is ``False``.
371
+ """
372
+
373
+ U12 = property(
374
+ lambda self: self._get_Uij(0, 1), lambda self, value: self._set_Uij(0, 1, value), doc=_doc_uij.format(0, 1)
375
+ )
376
+ U13 = property(
377
+ lambda self: self._get_Uij(0, 2), lambda self, value: self._set_Uij(0, 2, value), doc=_doc_uij.format(0, 2)
378
+ )
379
+ U23 = property(
380
+ lambda self: self._get_Uij(1, 2), lambda self, value: self._set_Uij(1, 2, value), doc=_doc_uij.format(1, 2)
381
+ )
382
+
383
+ # clean local variables
384
+ del _doc_uii, _doc_uij
385
+
386
+ # Uisoequiv
387
+
388
+ @property
389
+ def Uisoequiv(self):
390
+ """float : The isotropic displacement parameter or an equivalent value.
391
+
392
+ Setting a new value rescales tensor `U` so it yields equivalent
393
+ direction-averaged displacements.
394
+ """
395
+ if not self.anisotropy:
396
+ return self._U[0, 0]
397
+ if self.lattice is None:
398
+ return numpy.trace(self._U) / 3.0
399
+ lat = self.lattice
400
+ rv = (
401
+ 1.0
402
+ / 3.0
403
+ * (
404
+ self._U[0, 0] * lat.ar * lat.ar * lat.a * lat.a
405
+ + self._U[1, 1] * lat.br * lat.br * lat.b * lat.b
406
+ + self._U[2, 2] * lat.cr * lat.cr * lat.c * lat.c
407
+ + 2 * self._U[0, 1] * lat.ar * lat.br * lat.a * lat.b * lat.cg
408
+ + 2 * self._U[0, 2] * lat.ar * lat.cr * lat.a * lat.c * lat.cb
409
+ + 2 * self._U[1, 2] * lat.br * lat.cr * lat.b * lat.c * lat.ca
410
+ )
411
+ )
412
+ return rv
413
+
414
+ @Uisoequiv.setter
415
+ def Uisoequiv(self, value):
416
+ if self.anisotropy:
417
+ lat = self.lattice or cartesian_lattice
418
+ uequiv = self.Uisoequiv
419
+ if abs(uequiv) < lat._epsilon:
420
+ self._U = value * lat.isotropicunit
421
+ else:
422
+ self._U *= value / uequiv
423
+ else:
424
+ self._U[0, 0] = value
425
+ return
426
+
427
+ # Bij elements
428
+
429
+ # _doc_bii, _doc_bij are local variables.
430
+
431
+ _doc_bii = """
432
+ float : The ``B{0}{0}`` element of the Debye-Waller matrix.
433
+
434
+ This is equivalent to ``8 * pi**2 * U{0}{0}``. When `anisotropy`
435
+ is ``False`` setting a new value updates entire tensor `U`.
436
+ """
437
+
438
+ _doc_bij = """
439
+ float : The ``B{0}{1}`` element of the Debye-Waller matrix.
440
+
441
+ This is equivalent to ``8 * pi**2 * U{0}{1}``. Setting a new
442
+ value updates `U` in a symmetric way. Assignment has no effect
443
+ when `anisotropy` is ``False``.
444
+ """
445
+
446
+ B11 = property(
447
+ lambda self: _UtoB * self._get_Uij(0, 0),
448
+ lambda self, value: self._set_Uij(0, 0, _BtoU * value),
449
+ doc=_doc_bii.format(1),
450
+ )
451
+ B22 = property(
452
+ lambda self: _UtoB * self._get_Uij(1, 1),
453
+ lambda self, value: self._set_Uij(1, 1, _BtoU * value),
454
+ doc=_doc_bii.format(2),
455
+ )
456
+ B33 = property(
457
+ lambda self: _UtoB * self._get_Uij(2, 2),
458
+ lambda self, value: self._set_Uij(2, 2, _BtoU * value),
459
+ doc=_doc_bii.format(3),
460
+ )
461
+ B12 = property(
462
+ lambda self: _UtoB * self._get_Uij(0, 1),
463
+ lambda self, value: self._set_Uij(0, 1, _BtoU * value),
464
+ doc=_doc_bij.format(1, 2),
465
+ )
466
+ B13 = property(
467
+ lambda self: _UtoB * self._get_Uij(0, 2),
468
+ lambda self, value: self._set_Uij(0, 2, _BtoU * value),
469
+ doc=_doc_bij.format(1, 3),
470
+ )
471
+ B23 = property(
472
+ lambda self: _UtoB * self._get_Uij(1, 2),
473
+ lambda self, value: self._set_Uij(1, 2, _BtoU * value),
474
+ doc=_doc_bij.format(2, 3),
475
+ )
476
+
477
+ # clean local variables
478
+ del _doc_bii, _doc_bij
479
+
480
+ # Bisoequiv
481
+
482
+ @property
483
+ def Bisoequiv(self):
484
+ """float : The Debye-Waller isotropic displacement or an equivalent value.
485
+
486
+ This equals ``8 * pi**2 * Uisoequiv``. Setting a new value
487
+ rescales `U` tensor to yield equivalent direction-average of
488
+ Debye-Waller displacements.
489
+ """
490
+ return _UtoB * self.Uisoequiv
491
+
492
+ @Bisoequiv.setter
493
+ def Bisoequiv(self, value):
494
+ self.Uisoequiv = _BtoU * value
495
+ return
496
+
497
+
498
+ # End of class Atom
499
+
500
+ # Local Helpers --------------------------------------------------------------
501
+
502
+
503
+ class _AtomCartesianCoordinates(numpy.ndarray):
504
+ """Specialized `numpy.ndarray` for accessing Cartesian coordinates.
505
+
506
+ Inplace assignments to this array are applied on the *xyz* position
507
+ position of owner `Atom` as per the associated `Atom.lattice`.
508
+
509
+ Parameters
510
+ ----------
511
+ atom : Atom
512
+ `Atom` instance to be linked to these coordinate array.
513
+ """
514
+
515
+ def __new__(self, atom):
516
+ """Create the underlying numpy array base object."""
517
+ return numpy.empty(3, dtype=float).view(self)
518
+
519
+ def __init__(self, atom):
520
+ self._atom = atom
521
+ self.asarray[:] = atom.lattice.cartesian(atom.xyz)
522
+ return
523
+
524
+ @property
525
+ def asarray(self):
526
+ """ndarray : This array viewed as standard numpy array."""
527
+ return self.view(numpy.ndarray)
528
+
529
+ def __setitem__(self, idx, value):
530
+ """Set some element or slice of this Cartesian coordinates.
531
+
532
+ This overrides inplace array assignment to update the
533
+ *xyz* fractional coordinate of the linked `Atom`.
534
+ """
535
+ self.asarray[idx] = value
536
+ self._atom.xyz[:] = self._atom.lattice.fractional(self)
537
+ return
538
+
539
+ def __array_wrap__(self, out_arr, context=None):
540
+ """Ensure math operations on this type yield standard numpy array."""
541
+ return out_arr.view(numpy.ndarray)
542
+
543
+
544
+ # End of _AtomCartesianCoordinates
@@ -0,0 +1,27 @@
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: Chris Farrow, 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
+ """Methods and classes for manipulating `Structure` instances.
17
+
18
+ Package content:
19
+ * supercell -- create a supercell from an existing `Structure`.
20
+ """
21
+
22
+ # Import below whatever should be available at package namespace.
23
+
24
+ from diffpy.structure.expansion.supercell_mod import supercell
25
+
26
+ # silence pyflakes checker
27
+ assert supercell
@@ -0,0 +1,129 @@
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: Chris Farrow
10
+ #
11
+ # See AUTHORS.txt for a list of people who contributed.
12
+ # See LICENSE_DANSE.txt for license information.
13
+ #
14
+ ##############################################################################
15
+
16
+ """Make a spheroid nanoparticle from a template structure."""
17
+
18
+ from math import ceil
19
+
20
+ from numpy import array
21
+
22
+ from diffpy.structure import Structure
23
+ from diffpy.structure.expansion.shapeutils import findCenter
24
+
25
+
26
+ def makeSphere(S, radius):
27
+ """Create a spherical nanoparticle.
28
+
29
+ Parameters
30
+ ----------
31
+ S : Structure
32
+ A `Structure` instance.
33
+ radius : float
34
+ Primary equatorial radius (along x-axis).
35
+
36
+ Returns
37
+ -------
38
+ Structure
39
+ A new `Structure` instance.
40
+ """
41
+ return makeEllipsoid(S, radius)
42
+
43
+
44
+ def makeEllipsoid(S, a, b=None, c=None):
45
+ """
46
+ Cut a `Structure` out of another one.
47
+
48
+ Parameters
49
+ ----------
50
+ S : Structure
51
+ A `Structure` instance.
52
+ a : float
53
+ Primary equatorial radius (along x-axis).
54
+ b : float, Optional
55
+ Secondary equatorial radius (along y-axis). If `b` is ``None``
56
+ (default), then it is set equal to `a`.
57
+ c : float, Optional
58
+ Polar radius (along z-axis). If `c` is ``None`` (default), then it is
59
+ set equal to `a`.
60
+
61
+ Returns
62
+ -------
63
+ Structure :
64
+ A new `Structure` instance.
65
+ """
66
+ if b is None:
67
+ b = a
68
+ if c is None:
69
+ c = a
70
+ sabc = array([a, b, c])
71
+
72
+ # Create a supercell large enough for the ellipsoid
73
+ frac = S.lattice.fractional(sabc)
74
+ # FIXME - this looks fishy for non-orthogonal lattices
75
+ mno = max(ceil(2 * xi) for xi in frac) * array([1, 1, 1])
76
+ # Make the supercell
77
+ from diffpy.structure.expansion import supercell
78
+
79
+ newS = supercell(S, mno)
80
+ lat = newS.lattice
81
+
82
+ # Find the central atom
83
+ ncenter = findCenter(newS)
84
+
85
+ cxyz = lat.cartesian(newS[ncenter].xyz)
86
+
87
+ delList = []
88
+ N = len(newS)
89
+ j = N
90
+ for i in range(N):
91
+ j -= 1
92
+
93
+ # Calculate (x/a)**2 + (y/b)**2 + (z/c)**2
94
+ xyz = lat.cartesian(newS[j].xyz)
95
+ darray = ((xyz - cxyz) / sabc) ** 2
96
+ d = sum(darray) ** 0.5
97
+
98
+ # Discard atom if (x/a)**2 + (y/b)**2 + (z/c)**2 > 1
99
+ if d > 1:
100
+ delList.append(j)
101
+
102
+ for i in delList:
103
+ newS.pop(i)
104
+
105
+ return newS
106
+
107
+
108
+ # ----------------------------------------------------------------------------
109
+
110
+ if __name__ == "__main__":
111
+ import os.path
112
+
113
+ datadir = "../../tests/testdata"
114
+ S = Structure()
115
+ S.read(os.path.join(datadir, "CdSe_bulk.stru"), "pdffit")
116
+ newS = makeEllipsoid(S, 12)
117
+ newS.write("CdSe_d24.stru", "pdffit")
118
+ newS = makeEllipsoid(S, 20, 10, 10)
119
+ newS.write("CdSe_a20_b10_c10.stru", "pdffit")
120
+ newS = makeEllipsoid(S, 20, 15, 10)
121
+ newS.write("CdSe_a20_b15_c10.stru", "pdffit")
122
+ S = Structure()
123
+ S.read(os.path.join(datadir, "Ni.stru"), "pdffit")
124
+ newS = makeEllipsoid(S, 10)
125
+ newS.write("Ni_d20.stru", "pdffit")
126
+ newS = makeEllipsoid(S, 20, 4)
127
+ newS.write("Ni_a20_b4_c20.stru", "pdffit")
128
+ newS = makeEllipsoid(S, 20, 15, 10)
129
+ newS.write("Ni_a20_b15_c10.stru", "pdffit")