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.
- diffpy/Structure.py +35 -0
- diffpy/__init__.py +23 -0
- diffpy/structure/__init__.py +93 -0
- diffpy/structure/_legacy_importer.py +88 -0
- diffpy/structure/apps/__init__.py +17 -0
- diffpy/structure/apps/anyeye.py +284 -0
- diffpy/structure/apps/transtru.py +126 -0
- diffpy/structure/atom.py +544 -0
- diffpy/structure/expansion/__init__.py +27 -0
- diffpy/structure/expansion/makeellipsoid.py +129 -0
- diffpy/structure/expansion/shapeutils.py +44 -0
- diffpy/structure/expansion/supercell_mod.py +91 -0
- diffpy/structure/lattice.py +663 -0
- diffpy/structure/mmlibspacegroups.py +8154 -0
- diffpy/structure/parsers/__init__.py +83 -0
- diffpy/structure/parsers/p_auto.py +217 -0
- diffpy/structure/parsers/p_cif.py +876 -0
- diffpy/structure/parsers/p_discus.py +312 -0
- diffpy/structure/parsers/p_pdb.py +405 -0
- diffpy/structure/parsers/p_pdffit.py +290 -0
- diffpy/structure/parsers/p_rawxyz.py +149 -0
- diffpy/structure/parsers/p_xcfg.py +457 -0
- diffpy/structure/parsers/p_xyz.py +161 -0
- diffpy/structure/parsers/parser_index_mod.py +108 -0
- diffpy/structure/parsers/structureparser.py +80 -0
- diffpy/structure/pdffitstructure.py +109 -0
- diffpy/structure/sgtbxspacegroups.py +5198 -0
- diffpy/structure/spacegroupmod.py +329 -0
- diffpy/structure/spacegroups.py +1441 -0
- diffpy/structure/structure.py +866 -0
- diffpy/structure/structureerrors.py +35 -0
- diffpy/structure/symmetryutilities.py +1100 -0
- diffpy/structure/utils.py +126 -0
- diffpy/structure/version.py +26 -0
- diffpy.structure-3.2.0.dist-info/AUTHORS.rst +13 -0
- diffpy.structure-3.2.0.dist-info/LICENSE.rst +141 -0
- diffpy.structure-3.2.0.dist-info/LICENSE_DANSE.rst +50 -0
- diffpy.structure-3.2.0.dist-info/LICENSE_pymmlib.rst +203 -0
- diffpy.structure-3.2.0.dist-info/METADATA +197 -0
- diffpy.structure-3.2.0.dist-info/RECORD +42 -0
- diffpy.structure-3.2.0.dist-info/WHEEL +5 -0
- diffpy.structure-3.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,866 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##############################################################################
|
|
3
|
+
#
|
|
4
|
+
# diffpy.structure by DANSE Diffraction group
|
|
5
|
+
# Simon J. L. Billinge
|
|
6
|
+
# (c) 2007 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
|
+
"""This module defines class `Structure`.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import codecs
|
|
20
|
+
import copy as copymod
|
|
21
|
+
|
|
22
|
+
import numpy
|
|
23
|
+
|
|
24
|
+
from diffpy.structure.atom import Atom
|
|
25
|
+
from diffpy.structure.lattice import Lattice
|
|
26
|
+
from diffpy.structure.utils import _linkAtomAttribute, atomBareSymbol, isiterable
|
|
27
|
+
|
|
28
|
+
# ----------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Structure(list):
|
|
32
|
+
"""Define group of atoms in a specified lattice. Structure --> group of atoms.
|
|
33
|
+
|
|
34
|
+
`Structure` class is inherited from Python `list`. It contains
|
|
35
|
+
a list of `Atom` instances. `Structure` overloads `setitem` and `setslice`
|
|
36
|
+
methods so that the `lattice` attribute of atoms get set to `lattice`.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
atoms : list of Atom or Structure, Optional
|
|
41
|
+
List of `Atom` instances to be included in this `Structure`.
|
|
42
|
+
When `atoms` argument is an existing `Structure` instance,
|
|
43
|
+
the new structure is its copy.
|
|
44
|
+
lattice : Lattice, Optional
|
|
45
|
+
Instance of `Lattice` defining coordinate systems, property.
|
|
46
|
+
title : str, Optional
|
|
47
|
+
String description of the structure.
|
|
48
|
+
filename : str, Optional
|
|
49
|
+
Name of a file to load the structure from.
|
|
50
|
+
format : str, Optional
|
|
51
|
+
`Structure` format of the loaded `filename`. By default
|
|
52
|
+
all structure formats are tried one by one. Ignored when
|
|
53
|
+
`filename` has not been specified.
|
|
54
|
+
|
|
55
|
+
Note
|
|
56
|
+
----
|
|
57
|
+
Cannot use `filename` and `atoms` arguments together. Overrides `atoms` argument
|
|
58
|
+
when `filename` is specified.
|
|
59
|
+
|
|
60
|
+
Attributes
|
|
61
|
+
----------
|
|
62
|
+
title : str
|
|
63
|
+
String description of the structure.
|
|
64
|
+
lattice : Lattice
|
|
65
|
+
Instance of `Lattice` defining coordinate systems.
|
|
66
|
+
pdffit : None or dict
|
|
67
|
+
Dictionary of PDFFit-related metadata.
|
|
68
|
+
|
|
69
|
+
Examples
|
|
70
|
+
--------
|
|
71
|
+
``Structure(stru)`` create a copy of `Structure` instance stru.
|
|
72
|
+
|
|
73
|
+
>>> stru = Structure()
|
|
74
|
+
>>> copystru = Structure(stru)
|
|
75
|
+
|
|
76
|
+
`Structure` is inherited from a list it can use list expansions.
|
|
77
|
+
|
|
78
|
+
>>> oxygen_atoms = [a for a in stru if a.element == "O" ]
|
|
79
|
+
>>> oxygen_stru = Structure(oxygen_atoms, lattice=stru.lattice)
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
# default values for instance attributes
|
|
83
|
+
title = ""
|
|
84
|
+
"""str: default values for `title`."""
|
|
85
|
+
|
|
86
|
+
_lattice = None
|
|
87
|
+
pdffit = None
|
|
88
|
+
"""None: default values for `pdffit`."""
|
|
89
|
+
|
|
90
|
+
def __init__(self, atoms=None, lattice=None, title=None, filename=None, format=None):
|
|
91
|
+
# if filename is specified load it and return
|
|
92
|
+
if filename is not None:
|
|
93
|
+
if any((atoms, lattice, title)):
|
|
94
|
+
emsg = "Cannot use filename and atoms arguments together."
|
|
95
|
+
raise ValueError(emsg)
|
|
96
|
+
readkwargs = (format is not None) and {"format": format} or {}
|
|
97
|
+
self.read(filename, **readkwargs)
|
|
98
|
+
return
|
|
99
|
+
# copy initialization, must be first to allow lattice, title override
|
|
100
|
+
if isinstance(atoms, Structure):
|
|
101
|
+
Structure.__copy__(atoms, self)
|
|
102
|
+
# assign arguments:
|
|
103
|
+
if title is not None:
|
|
104
|
+
self.title = title
|
|
105
|
+
if lattice is not None:
|
|
106
|
+
self.lattice = lattice
|
|
107
|
+
elif self.lattice is None:
|
|
108
|
+
self.lattice = Lattice()
|
|
109
|
+
# insert atoms unless already done by __copy__
|
|
110
|
+
if not len(self) and atoms is not None:
|
|
111
|
+
self.extend(atoms)
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
def copy(self):
|
|
115
|
+
"""Return a copy of this `Structure` object."""
|
|
116
|
+
return copymod.copy(self)
|
|
117
|
+
|
|
118
|
+
def __copy__(self, target=None):
|
|
119
|
+
"""Create a deep copy of this instance.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
----------
|
|
123
|
+
target :
|
|
124
|
+
Optional target instance for copying, useful for
|
|
125
|
+
copying a derived class. Defaults to new instance
|
|
126
|
+
of the same type as self.
|
|
127
|
+
|
|
128
|
+
Returns
|
|
129
|
+
-------
|
|
130
|
+
A duplicate instance of this object.
|
|
131
|
+
"""
|
|
132
|
+
if target is None:
|
|
133
|
+
target = Structure()
|
|
134
|
+
elif target is self:
|
|
135
|
+
return target
|
|
136
|
+
# copy attributes as appropriate:
|
|
137
|
+
target.title = self.title
|
|
138
|
+
target.lattice = Lattice(self.lattice)
|
|
139
|
+
target.pdffit = copymod.deepcopy(self.pdffit)
|
|
140
|
+
# copy all atoms to the target
|
|
141
|
+
target[:] = self
|
|
142
|
+
return target
|
|
143
|
+
|
|
144
|
+
def __str__(self):
|
|
145
|
+
"""Simple string representation."""
|
|
146
|
+
s_lattice = "lattice=%s" % self.lattice
|
|
147
|
+
s_atoms = "\n".join([str(a) for a in self])
|
|
148
|
+
return s_lattice + "\n" + s_atoms
|
|
149
|
+
|
|
150
|
+
def addNewAtom(self, *args, **kwargs):
|
|
151
|
+
"""Add new `Atom` instance to the end of this `Structure`.
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
*args, **kwargs :
|
|
156
|
+
See `Atom` class constructor.
|
|
157
|
+
"""
|
|
158
|
+
kwargs["lattice"] = self.lattice
|
|
159
|
+
a = Atom(*args, **kwargs)
|
|
160
|
+
self.append(a, copy=False)
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
def getLastAtom(self):
|
|
164
|
+
"""Return Reference to the last `Atom` in this structure."""
|
|
165
|
+
last_atom = self[-1]
|
|
166
|
+
return last_atom
|
|
167
|
+
|
|
168
|
+
def assignUniqueLabels(self):
|
|
169
|
+
"""Set a unique label string for each `Atom` in this structure.
|
|
170
|
+
|
|
171
|
+
The label strings are formatted as "%(baresymbol)s%(index)i",
|
|
172
|
+
where baresymbol is the element right-stripped of "[0-9][+-]".
|
|
173
|
+
"""
|
|
174
|
+
elnum = {}
|
|
175
|
+
# support duplicate atom instances
|
|
176
|
+
islabeled = set()
|
|
177
|
+
for a in self:
|
|
178
|
+
if a in islabeled:
|
|
179
|
+
continue
|
|
180
|
+
baresmbl = atomBareSymbol(a.element)
|
|
181
|
+
elnum[baresmbl] = elnum.get(baresmbl, 0) + 1
|
|
182
|
+
a.label = baresmbl + str(elnum[baresmbl])
|
|
183
|
+
islabeled.add(a)
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
def distance(self, aid0, aid1):
|
|
187
|
+
"""Calculate distance between 2 `Atoms`, no periodic boundary conditions.
|
|
188
|
+
|
|
189
|
+
Parameters
|
|
190
|
+
----------
|
|
191
|
+
aid0 : int or str
|
|
192
|
+
Zero based index of the first `Atom` or a string label.
|
|
193
|
+
aid1 : int or str
|
|
194
|
+
Zero based index or string label of the second atom.
|
|
195
|
+
|
|
196
|
+
Returns
|
|
197
|
+
-------
|
|
198
|
+
float
|
|
199
|
+
Distance between the two `Atoms` in Angstroms.
|
|
200
|
+
|
|
201
|
+
Raises
|
|
202
|
+
------
|
|
203
|
+
IndexError
|
|
204
|
+
If any of the `Atom` indices or labels are invalid.
|
|
205
|
+
"""
|
|
206
|
+
# lookup by labels
|
|
207
|
+
a0, a1 = self[aid0, aid1]
|
|
208
|
+
return self.lattice.dist(a0.xyz, a1.xyz)
|
|
209
|
+
|
|
210
|
+
def angle(self, aid0, aid1, aid2):
|
|
211
|
+
"""
|
|
212
|
+
The bond angle at the second of three `Atoms` in degrees.
|
|
213
|
+
|
|
214
|
+
Parameters
|
|
215
|
+
----------
|
|
216
|
+
aid0 : int or str
|
|
217
|
+
Zero based index of the first `Atom` or a string label.
|
|
218
|
+
aid1 : int or str
|
|
219
|
+
Index or string label for the second atom, where the angle is formed.
|
|
220
|
+
aid2 : int or str
|
|
221
|
+
Index or string label for the third atom.
|
|
222
|
+
|
|
223
|
+
Returns
|
|
224
|
+
-------
|
|
225
|
+
float
|
|
226
|
+
The bond angle in degrees.
|
|
227
|
+
|
|
228
|
+
Raises
|
|
229
|
+
------
|
|
230
|
+
IndexError
|
|
231
|
+
If any of the arguments are invalid.
|
|
232
|
+
"""
|
|
233
|
+
a0, a1, a2 = self[aid0, aid1, aid2]
|
|
234
|
+
u10 = a0.xyz - a1.xyz
|
|
235
|
+
u12 = a2.xyz - a1.xyz
|
|
236
|
+
return self.lattice.angle(u10, u12)
|
|
237
|
+
|
|
238
|
+
def placeInLattice(self, new_lattice):
|
|
239
|
+
"""place structure into `new_lattice` coordinate system.
|
|
240
|
+
|
|
241
|
+
Sets `lattice` to `new_lattice` and recalculate fractional coordinates
|
|
242
|
+
of all `Atoms` so their absolute positions remain the same.
|
|
243
|
+
|
|
244
|
+
Parameters
|
|
245
|
+
----------
|
|
246
|
+
new_lattice : Lattice
|
|
247
|
+
New `lattice` to place the structure into.
|
|
248
|
+
|
|
249
|
+
Returns
|
|
250
|
+
-------
|
|
251
|
+
Structure
|
|
252
|
+
Reference to this `Structure` object. The `lattice` attribute
|
|
253
|
+
is updated to `new_lattice`.
|
|
254
|
+
"""
|
|
255
|
+
Tx = numpy.dot(self.lattice.base, new_lattice.recbase)
|
|
256
|
+
Tu = numpy.dot(self.lattice.normbase, new_lattice.recnormbase)
|
|
257
|
+
for a in self:
|
|
258
|
+
a.xyz = numpy.dot(a.xyz, Tx)
|
|
259
|
+
if a.anisotropy:
|
|
260
|
+
a.U = numpy.dot(numpy.transpose(Tu), numpy.dot(a.U, Tu))
|
|
261
|
+
self.lattice = new_lattice
|
|
262
|
+
return self
|
|
263
|
+
|
|
264
|
+
def read(self, filename, format="auto"):
|
|
265
|
+
"""Load structure from a file, any original data become lost.
|
|
266
|
+
|
|
267
|
+
Parameters
|
|
268
|
+
----------
|
|
269
|
+
filename : str
|
|
270
|
+
File to be loaded.
|
|
271
|
+
format : str, Optional
|
|
272
|
+
All structure formats are defined in parsers submodule,
|
|
273
|
+
when ``format == 'auto'`` all parsers are tried one by one.
|
|
274
|
+
|
|
275
|
+
Returns
|
|
276
|
+
-------
|
|
277
|
+
Parser
|
|
278
|
+
Return instance of data Parser used to process input string. This
|
|
279
|
+
can be inspected for information related to particular format.
|
|
280
|
+
"""
|
|
281
|
+
import diffpy.structure
|
|
282
|
+
import diffpy.structure.parsers
|
|
283
|
+
|
|
284
|
+
getParser = diffpy.structure.parsers.getParser
|
|
285
|
+
p = getParser(format)
|
|
286
|
+
new_structure = p.parseFile(filename)
|
|
287
|
+
# reinitialize data after successful parsing
|
|
288
|
+
# avoid calling __init__ from a derived class
|
|
289
|
+
Structure.__init__(self)
|
|
290
|
+
if new_structure is not None:
|
|
291
|
+
self.__dict__.update(new_structure.__dict__)
|
|
292
|
+
self[:] = new_structure
|
|
293
|
+
if not self.title:
|
|
294
|
+
import os.path
|
|
295
|
+
|
|
296
|
+
tailname = os.path.basename(filename)
|
|
297
|
+
tailbase = os.path.splitext(tailname)[0]
|
|
298
|
+
self.title = tailbase
|
|
299
|
+
return p
|
|
300
|
+
|
|
301
|
+
def readStr(self, s, format="auto"):
|
|
302
|
+
"""Read structure from a string.
|
|
303
|
+
|
|
304
|
+
Parameters
|
|
305
|
+
----------
|
|
306
|
+
s : str
|
|
307
|
+
String with structure definition.
|
|
308
|
+
format : str, Optional
|
|
309
|
+
All structure formats are defined in parsers submodule. When ``format == 'auto'``,
|
|
310
|
+
all parsers are tried one by one.
|
|
311
|
+
|
|
312
|
+
Returns
|
|
313
|
+
-------
|
|
314
|
+
Parser
|
|
315
|
+
Return instance of data Parser used to process input string. This
|
|
316
|
+
can be inspected for information related to particular format.
|
|
317
|
+
"""
|
|
318
|
+
from diffpy.structure.parsers import getParser
|
|
319
|
+
|
|
320
|
+
p = getParser(format)
|
|
321
|
+
new_structure = p.parse(s)
|
|
322
|
+
# reinitialize data after successful parsing
|
|
323
|
+
# avoid calling __init__ from a derived class
|
|
324
|
+
Structure.__init__(self)
|
|
325
|
+
if new_structure is not None:
|
|
326
|
+
self.__dict__.update(new_structure.__dict__)
|
|
327
|
+
self[:] = new_structure
|
|
328
|
+
return p
|
|
329
|
+
|
|
330
|
+
def write(self, filename, format):
|
|
331
|
+
"""Save structure to file in the specified format.
|
|
332
|
+
|
|
333
|
+
Parameters
|
|
334
|
+
----------
|
|
335
|
+
filename : str
|
|
336
|
+
File to save the structure to.
|
|
337
|
+
format : str
|
|
338
|
+
`Structure` format to use for saving.
|
|
339
|
+
|
|
340
|
+
Note
|
|
341
|
+
----
|
|
342
|
+
Available structure formats can be obtained by:
|
|
343
|
+
|
|
344
|
+
``from parsers import formats``
|
|
345
|
+
"""
|
|
346
|
+
from diffpy.structure.parsers import getParser
|
|
347
|
+
|
|
348
|
+
p = getParser(format)
|
|
349
|
+
p.filename = filename
|
|
350
|
+
s = p.tostring(self)
|
|
351
|
+
with codecs.open(filename, "w", encoding="UTF-8") as fp:
|
|
352
|
+
fp.write(s)
|
|
353
|
+
return
|
|
354
|
+
|
|
355
|
+
def writeStr(self, format):
|
|
356
|
+
"""return string representation of the structure in specified format.
|
|
357
|
+
|
|
358
|
+
Note
|
|
359
|
+
----
|
|
360
|
+
Available structure formats can be obtained by:
|
|
361
|
+
|
|
362
|
+
``from parsers import formats``
|
|
363
|
+
"""
|
|
364
|
+
from diffpy.structure.parsers import getParser
|
|
365
|
+
|
|
366
|
+
p = getParser(format)
|
|
367
|
+
s = p.tostring(self)
|
|
368
|
+
return s
|
|
369
|
+
|
|
370
|
+
def tolist(self):
|
|
371
|
+
"""Return `Atoms` in this `Structure` as a standard Python list."""
|
|
372
|
+
rv = [a for a in self]
|
|
373
|
+
return rv
|
|
374
|
+
|
|
375
|
+
# Overloaded list Methods and Operators ----------------------------------
|
|
376
|
+
|
|
377
|
+
def append(self, a, copy=True):
|
|
378
|
+
"""Append `Atom` to a structure and update its `lattice` attribute.
|
|
379
|
+
|
|
380
|
+
Parameters
|
|
381
|
+
----------
|
|
382
|
+
a : Atom
|
|
383
|
+
Instance of `Atom` to be appended.
|
|
384
|
+
copy : bool, Optional
|
|
385
|
+
Flag for appending a copy of `a`. When ``False``, append `a` and update `a.lattice`.
|
|
386
|
+
"""
|
|
387
|
+
adup = copy and Atom(a) or a
|
|
388
|
+
adup.lattice = self.lattice
|
|
389
|
+
super(Structure, self).append(adup)
|
|
390
|
+
return
|
|
391
|
+
|
|
392
|
+
def insert(self, idx, a, copy=True):
|
|
393
|
+
"""Insert `Atom` a before position idx in this `Structure`.
|
|
394
|
+
|
|
395
|
+
Parameters
|
|
396
|
+
----------
|
|
397
|
+
idx : int
|
|
398
|
+
Position in `Atom` list.
|
|
399
|
+
a : Atom
|
|
400
|
+
Instance of `Atom` to be inserted.
|
|
401
|
+
copy : bool, Optional
|
|
402
|
+
Flag for inserting a copy of `a`. When ``False``, append `a` and update `a.lattice`.
|
|
403
|
+
"""
|
|
404
|
+
adup = copy and copymod.copy(a) or a
|
|
405
|
+
adup.lattice = self.lattice
|
|
406
|
+
super(Structure, self).insert(idx, adup)
|
|
407
|
+
return
|
|
408
|
+
|
|
409
|
+
def extend(self, atoms, copy=None):
|
|
410
|
+
"""Extend `Structure` with an iterable of `atoms`.
|
|
411
|
+
|
|
412
|
+
Update the `lattice` attribute of all added `atoms`.
|
|
413
|
+
|
|
414
|
+
Parameters
|
|
415
|
+
----------
|
|
416
|
+
atoms : Iterable
|
|
417
|
+
The `Atom` objects to be appended to this `Structure`.
|
|
418
|
+
copy : bool, Optional
|
|
419
|
+
Flag for adding copies of `Atom` objects.
|
|
420
|
+
Make copies when ``True``, append `atoms` unchanged when ``False``.
|
|
421
|
+
The default behavior is to make copies when `atoms` are of
|
|
422
|
+
`Structure` type or if new atoms introduce repeated objects.
|
|
423
|
+
"""
|
|
424
|
+
adups = (copymod.copy(a) for a in atoms)
|
|
425
|
+
if copy is None:
|
|
426
|
+
if isinstance(atoms, Structure):
|
|
427
|
+
newatoms = adups
|
|
428
|
+
else:
|
|
429
|
+
memo = set(id(a) for a in self)
|
|
430
|
+
|
|
431
|
+
def nextatom(a):
|
|
432
|
+
return a if id(a) not in memo else copymod.copy(a)
|
|
433
|
+
|
|
434
|
+
def mark(a):
|
|
435
|
+
return (memo.add(id(a)), a)[-1]
|
|
436
|
+
|
|
437
|
+
newatoms = (mark(nextatom(a)) for a in atoms)
|
|
438
|
+
elif copy:
|
|
439
|
+
newatoms = adups
|
|
440
|
+
else:
|
|
441
|
+
newatoms = atoms
|
|
442
|
+
|
|
443
|
+
def setlat(a):
|
|
444
|
+
return (setattr(a, "lattice", self.lattice), a)[-1]
|
|
445
|
+
|
|
446
|
+
super(Structure, self).extend(setlat(a) for a in newatoms)
|
|
447
|
+
return
|
|
448
|
+
|
|
449
|
+
def __getitem__(self, idx):
|
|
450
|
+
"""Get one or more `Atoms` in this structure.
|
|
451
|
+
|
|
452
|
+
Parameters
|
|
453
|
+
----------
|
|
454
|
+
idx : int ot str ot Iterable
|
|
455
|
+
`Atom` identifier. When integer use standard list lookup.
|
|
456
|
+
For iterables use numpy lookup, this supports integer or
|
|
457
|
+
boolean flag arrays. For string or string-containing iterables
|
|
458
|
+
lookup the `Atoms` by string label.
|
|
459
|
+
|
|
460
|
+
Returns
|
|
461
|
+
-------
|
|
462
|
+
Atom or Structure
|
|
463
|
+
An `Atom` instance for integer or string index or a substructure
|
|
464
|
+
in all other cases.
|
|
465
|
+
|
|
466
|
+
Raises
|
|
467
|
+
------
|
|
468
|
+
IndexError
|
|
469
|
+
If the index is invalid or the `Atom` label is not unique.
|
|
470
|
+
|
|
471
|
+
Examples
|
|
472
|
+
--------
|
|
473
|
+
First `Atom` in the `Structure`:
|
|
474
|
+
|
|
475
|
+
>>> stru[0]
|
|
476
|
+
|
|
477
|
+
Substructure of all ``'Na'`` `Atoms`:
|
|
478
|
+
|
|
479
|
+
>>> stru[stru.element == 'Na']
|
|
480
|
+
|
|
481
|
+
`Atom` with a unique label ``'Na3'``:
|
|
482
|
+
>>> stru['Na3']
|
|
483
|
+
|
|
484
|
+
Substructure of three `Atoms`, lookup by label is more efficient
|
|
485
|
+
when done for several `Atoms` at once.
|
|
486
|
+
|
|
487
|
+
>>> stru['Na3', 2, 'Cl2']
|
|
488
|
+
"""
|
|
489
|
+
if isinstance(idx, slice):
|
|
490
|
+
rv = self.__emptySharedStructure()
|
|
491
|
+
lst = super(Structure, self).__getitem__(idx)
|
|
492
|
+
rv.extend(lst, copy=False)
|
|
493
|
+
return rv
|
|
494
|
+
try:
|
|
495
|
+
rv = super(Structure, self).__getitem__(idx)
|
|
496
|
+
return rv
|
|
497
|
+
except TypeError:
|
|
498
|
+
pass
|
|
499
|
+
# check if there is any string label that should be resolved
|
|
500
|
+
scalarstringlabel = isinstance(idx, str)
|
|
501
|
+
hasstringlabel = scalarstringlabel or (isiterable(idx) and any(isinstance(ii, str) for ii in idx))
|
|
502
|
+
# if not, use numpy indexing to resolve idx
|
|
503
|
+
if not hasstringlabel:
|
|
504
|
+
idx1 = idx
|
|
505
|
+
if type(idx) is tuple:
|
|
506
|
+
idx1 = numpy.r_[idx]
|
|
507
|
+
indices = numpy.arange(len(self))[idx1]
|
|
508
|
+
rhs = [list.__getitem__(self, i) for i in indices]
|
|
509
|
+
rv = self.__emptySharedStructure()
|
|
510
|
+
rv.extend(rhs, copy=False)
|
|
511
|
+
return rv
|
|
512
|
+
# here we need to resolve at least one string label
|
|
513
|
+
# build a map of labels to indices and mark duplicate labels
|
|
514
|
+
duplicate = object()
|
|
515
|
+
labeltoindex = {}
|
|
516
|
+
for i, a in enumerate(self):
|
|
517
|
+
labeltoindex[a.label] = duplicate if a.label in labeltoindex else i
|
|
518
|
+
|
|
519
|
+
def _resolveindex(aid):
|
|
520
|
+
aid1 = aid
|
|
521
|
+
if type(aid) is str:
|
|
522
|
+
aid1 = labeltoindex.get(aid, None)
|
|
523
|
+
if aid1 is None:
|
|
524
|
+
raise IndexError("Invalid atom label %r." % aid)
|
|
525
|
+
if aid1 is duplicate:
|
|
526
|
+
raise IndexError("Atom label %r is not unique." % aid)
|
|
527
|
+
return aid1
|
|
528
|
+
|
|
529
|
+
# generate new index object that has no strings
|
|
530
|
+
if scalarstringlabel:
|
|
531
|
+
idx2 = _resolveindex(idx)
|
|
532
|
+
# for iterables preserve the tuple object type
|
|
533
|
+
else:
|
|
534
|
+
idx2 = [_resolveindex(i) for i in idx]
|
|
535
|
+
if type(idx) is tuple:
|
|
536
|
+
idx2 = tuple(idx2)
|
|
537
|
+
# call this function again and hope there is no recursion loop
|
|
538
|
+
rv = self[idx2]
|
|
539
|
+
return rv
|
|
540
|
+
|
|
541
|
+
def __setitem__(self, idx, value, copy=True):
|
|
542
|
+
"""Assign `self[idx]` `Atom` to value.
|
|
543
|
+
|
|
544
|
+
Parameters
|
|
545
|
+
----------
|
|
546
|
+
idx : int or slice
|
|
547
|
+
Index of `Atom` in this `Structure` or a slice.
|
|
548
|
+
value : Atom or Iterable
|
|
549
|
+
Instance of `Atom` or an iterable.
|
|
550
|
+
copy : bool, Optional
|
|
551
|
+
Flag for making a copy of the value. When ``False``, update
|
|
552
|
+
the `lattice` attribute of `Atom` objects present in value.
|
|
553
|
+
Default is ``True``.
|
|
554
|
+
"""
|
|
555
|
+
# handle slice assignment
|
|
556
|
+
if isinstance(idx, slice):
|
|
557
|
+
|
|
558
|
+
def _fixlat(a):
|
|
559
|
+
a.lattice = self.lattice
|
|
560
|
+
return a
|
|
561
|
+
|
|
562
|
+
v1 = value
|
|
563
|
+
if copy:
|
|
564
|
+
keep = set(super(Structure, self).__getitem__(idx))
|
|
565
|
+
v1 = (a if a in keep else Atom(a) for a in value)
|
|
566
|
+
vfinal = filter(_fixlat, v1)
|
|
567
|
+
# handle scalar assingment
|
|
568
|
+
else:
|
|
569
|
+
vfinal = Atom(value) if copy else value
|
|
570
|
+
vfinal.lattice = self.lattice
|
|
571
|
+
super(Structure, self).__setitem__(idx, vfinal)
|
|
572
|
+
return
|
|
573
|
+
|
|
574
|
+
def __add__(self, other):
|
|
575
|
+
"""Return new `Structure` object with appended `Atoms` from other.
|
|
576
|
+
|
|
577
|
+
Parameters
|
|
578
|
+
----------
|
|
579
|
+
other : sequence of Atom
|
|
580
|
+
Sequence of `Atom` instances.
|
|
581
|
+
|
|
582
|
+
Returns
|
|
583
|
+
-------
|
|
584
|
+
Structure
|
|
585
|
+
New `Structure` with a copy of `Atom` instances.
|
|
586
|
+
"""
|
|
587
|
+
rv = copymod.copy(self)
|
|
588
|
+
rv += other
|
|
589
|
+
return rv
|
|
590
|
+
|
|
591
|
+
def __iadd__(self, other):
|
|
592
|
+
"""Extend this `Structure` with `Atoms` from other.
|
|
593
|
+
|
|
594
|
+
Parameters
|
|
595
|
+
----------
|
|
596
|
+
other : sequence of Atom
|
|
597
|
+
Sequence of `Atom` instances.
|
|
598
|
+
|
|
599
|
+
Returns
|
|
600
|
+
-------
|
|
601
|
+
Structure
|
|
602
|
+
Reference to this `Structure` object.
|
|
603
|
+
"""
|
|
604
|
+
self.extend(other, copy=True)
|
|
605
|
+
return self
|
|
606
|
+
|
|
607
|
+
def __sub__(self, other):
|
|
608
|
+
"""Return new `Structure` that has `Atoms` from the other removed.
|
|
609
|
+
|
|
610
|
+
Parameters
|
|
611
|
+
----------
|
|
612
|
+
other : sequence of Atom
|
|
613
|
+
Sequence of `Atom` instances.
|
|
614
|
+
|
|
615
|
+
Returns
|
|
616
|
+
-------
|
|
617
|
+
Structure
|
|
618
|
+
New `Structure` with a copy of `Atom` instances.
|
|
619
|
+
"""
|
|
620
|
+
otherset = set(other)
|
|
621
|
+
keepindices = [i for i, a in enumerate(self) if a not in otherset]
|
|
622
|
+
rv = copymod.copy(self[keepindices])
|
|
623
|
+
return rv
|
|
624
|
+
|
|
625
|
+
def __isub__(self, other):
|
|
626
|
+
"""Remove other `Atoms` if present in this structure.
|
|
627
|
+
|
|
628
|
+
Parameters
|
|
629
|
+
----------
|
|
630
|
+
other : sequence of Atom
|
|
631
|
+
Sequence of `Atom` instances.
|
|
632
|
+
|
|
633
|
+
Returns
|
|
634
|
+
-------
|
|
635
|
+
Structure
|
|
636
|
+
Reference to this `Structure` object.
|
|
637
|
+
"""
|
|
638
|
+
otherset = set(other)
|
|
639
|
+
self[:] = [a for a in self if a not in otherset]
|
|
640
|
+
return self
|
|
641
|
+
|
|
642
|
+
def __mul__(self, n):
|
|
643
|
+
"""Return new `Structure` with n-times concatenated `Atoms` from self.
|
|
644
|
+
`Atoms` and `lattice` in the new structure are all copies.
|
|
645
|
+
|
|
646
|
+
Parameters
|
|
647
|
+
----------
|
|
648
|
+
n : int
|
|
649
|
+
Integer multiple.
|
|
650
|
+
|
|
651
|
+
Returns
|
|
652
|
+
-------
|
|
653
|
+
Structure
|
|
654
|
+
New `Structure` with n-times concatenated `Atoms`.
|
|
655
|
+
"""
|
|
656
|
+
rv = copymod.copy(self[:0])
|
|
657
|
+
rv += n * self.tolist()
|
|
658
|
+
return rv
|
|
659
|
+
|
|
660
|
+
# right-side multiplication is the same as left-side
|
|
661
|
+
__rmul__ = __mul__
|
|
662
|
+
|
|
663
|
+
def __imul__(self, n):
|
|
664
|
+
"""Concatenate this `Structure` to n-times more `Atoms`.
|
|
665
|
+
For positive multiple the current `Atom` objects remain at the
|
|
666
|
+
beginning of this `Structure`.
|
|
667
|
+
|
|
668
|
+
Parameters
|
|
669
|
+
----------
|
|
670
|
+
n : int
|
|
671
|
+
Integer multiple.
|
|
672
|
+
|
|
673
|
+
Returns
|
|
674
|
+
-------
|
|
675
|
+
Structure
|
|
676
|
+
Reference to this `Structure` object.
|
|
677
|
+
"""
|
|
678
|
+
if n <= 0:
|
|
679
|
+
self[:] = []
|
|
680
|
+
else:
|
|
681
|
+
self.extend((n - 1) * self.tolist(), copy=True)
|
|
682
|
+
return self
|
|
683
|
+
|
|
684
|
+
# Properties -------------------------------------------------------------
|
|
685
|
+
|
|
686
|
+
# lattice
|
|
687
|
+
|
|
688
|
+
def _get_lattice(self):
|
|
689
|
+
return self._lattice
|
|
690
|
+
|
|
691
|
+
def _set_lattice(self, value):
|
|
692
|
+
for a in self:
|
|
693
|
+
a.lattice = value
|
|
694
|
+
self._lattice = value
|
|
695
|
+
return
|
|
696
|
+
|
|
697
|
+
lattice = property(_get_lattice, _set_lattice, doc="Coordinate system for this `Structure`.")
|
|
698
|
+
|
|
699
|
+
# composition
|
|
700
|
+
|
|
701
|
+
def _get_composition(self):
|
|
702
|
+
rv = {}
|
|
703
|
+
for a in self:
|
|
704
|
+
rv[a.element] = rv.get(a.element, 0.0) + a.occupancy
|
|
705
|
+
return rv
|
|
706
|
+
|
|
707
|
+
composition = property(_get_composition, doc="Dictionary of chemical symbols and their total occupancies.")
|
|
708
|
+
|
|
709
|
+
# linked atom attributes
|
|
710
|
+
|
|
711
|
+
element = _linkAtomAttribute(
|
|
712
|
+
"element",
|
|
713
|
+
"""Character array of `Atom` types. Assignment updates
|
|
714
|
+
the element attribute of the respective `Atoms`.""",
|
|
715
|
+
toarray=numpy.char.array,
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
xyz = _linkAtomAttribute(
|
|
719
|
+
"xyz",
|
|
720
|
+
"""Array of fractional coordinates of all `Atoms`.
|
|
721
|
+
Assignment updates `xyz` attribute of all `Atoms`.""",
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
x = _linkAtomAttribute(
|
|
725
|
+
"x",
|
|
726
|
+
"""Array of all fractional coordinates `x`.
|
|
727
|
+
Assignment updates `xyz` attribute of all `Atoms`.""",
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
y = _linkAtomAttribute(
|
|
731
|
+
"y",
|
|
732
|
+
"""Array of all fractional coordinates `y`.
|
|
733
|
+
Assignment updates `xyz` attribute of all `Atoms`.""",
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
z = _linkAtomAttribute(
|
|
737
|
+
"z",
|
|
738
|
+
"""Array of all fractional coordinates `z`.
|
|
739
|
+
Assignment updates `xyz` attribute of all `Atoms`.""",
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
label = _linkAtomAttribute(
|
|
743
|
+
"label",
|
|
744
|
+
"""Character array of `Atom` names. Assignment updates
|
|
745
|
+
the label attribute of all `Atoms`.""",
|
|
746
|
+
toarray=numpy.char.array,
|
|
747
|
+
)
|
|
748
|
+
|
|
749
|
+
occupancy = _linkAtomAttribute(
|
|
750
|
+
"occupancy",
|
|
751
|
+
"""Array of `Atom` occupancies. Assignment updates the
|
|
752
|
+
occupancy attribute of all `Atoms`.""",
|
|
753
|
+
)
|
|
754
|
+
|
|
755
|
+
xyz_cartn = _linkAtomAttribute(
|
|
756
|
+
"xyz_cartn",
|
|
757
|
+
"""Array of absolute Cartesian coordinates of all `Atoms`.
|
|
758
|
+
Assignment updates the `xyz` attribute of all `Atoms`.""",
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
anisotropy = _linkAtomAttribute(
|
|
762
|
+
"anisotropy",
|
|
763
|
+
"""Boolean array for anisotropic thermal displacement flags.
|
|
764
|
+
Assignment updates the anisotropy attribute of all `Atoms`.""",
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
U = _linkAtomAttribute(
|
|
768
|
+
"U",
|
|
769
|
+
"""Array of anisotropic thermal displacement tensors.
|
|
770
|
+
Assignment updates the U and anisotropy attributes of all `Atoms`.""",
|
|
771
|
+
)
|
|
772
|
+
|
|
773
|
+
Uisoequiv = _linkAtomAttribute(
|
|
774
|
+
"Uisoequiv",
|
|
775
|
+
"""Array of isotropic thermal displacement or equivalent values.
|
|
776
|
+
Assignment updates the U attribute of all `Atoms`.""",
|
|
777
|
+
)
|
|
778
|
+
|
|
779
|
+
U11 = _linkAtomAttribute(
|
|
780
|
+
"U11",
|
|
781
|
+
"""Array of `U11` elements of the anisotropic displacement tensors.
|
|
782
|
+
Assignment updates the U and anisotropy attributes of all `Atoms`.""",
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
U22 = _linkAtomAttribute(
|
|
786
|
+
"U22",
|
|
787
|
+
"""Array of `U22` elements of the anisotropic displacement tensors.
|
|
788
|
+
Assignment updates the U and anisotropy attributes of all `Atoms`.""",
|
|
789
|
+
)
|
|
790
|
+
|
|
791
|
+
U33 = _linkAtomAttribute(
|
|
792
|
+
"U33",
|
|
793
|
+
"""Array of `U33` elements of the anisotropic displacement tensors.
|
|
794
|
+
Assignment updates the U and anisotropy attributes of all `Atoms`.""",
|
|
795
|
+
)
|
|
796
|
+
|
|
797
|
+
U12 = _linkAtomAttribute(
|
|
798
|
+
"U12",
|
|
799
|
+
"""Array of `U12` elements of the anisotropic displacement tensors.
|
|
800
|
+
Assignment updates the U and anisotropy attributes of all `Atoms`.""",
|
|
801
|
+
)
|
|
802
|
+
|
|
803
|
+
U13 = _linkAtomAttribute(
|
|
804
|
+
"U13",
|
|
805
|
+
"""Array of `U13` elements of the anisotropic displacement tensors.
|
|
806
|
+
Assignment updates the U and anisotropy attributes of all `Atoms`.""",
|
|
807
|
+
)
|
|
808
|
+
|
|
809
|
+
U23 = _linkAtomAttribute(
|
|
810
|
+
"U23",
|
|
811
|
+
"""Array of `U23` elements of the anisotropic displacement tensors.
|
|
812
|
+
Assignment updates the U and anisotropy attributes of all `Atoms`.""",
|
|
813
|
+
)
|
|
814
|
+
|
|
815
|
+
Bisoequiv = _linkAtomAttribute(
|
|
816
|
+
"Bisoequiv",
|
|
817
|
+
"""Array of Debye-Waller isotropic thermal displacement or equivalent
|
|
818
|
+
values. Assignment updates the U attribute of all `Atoms`.""",
|
|
819
|
+
)
|
|
820
|
+
|
|
821
|
+
B11 = _linkAtomAttribute(
|
|
822
|
+
"B11",
|
|
823
|
+
"""Array of `B11` elements of the Debye-Waller displacement tensors.
|
|
824
|
+
Assignment updates the U and anisotropy attributes of all `Atoms`.""",
|
|
825
|
+
)
|
|
826
|
+
|
|
827
|
+
B22 = _linkAtomAttribute(
|
|
828
|
+
"B22",
|
|
829
|
+
"""Array of `B22` elements of the Debye-Waller displacement tensors.
|
|
830
|
+
Assignment updates the U and anisotropy attributes of all `Atoms`.""",
|
|
831
|
+
)
|
|
832
|
+
|
|
833
|
+
B33 = _linkAtomAttribute(
|
|
834
|
+
"B33",
|
|
835
|
+
"""Array of `B33` elements of the Debye-Waller displacement tensors.
|
|
836
|
+
Assignment updates the U and anisotropy attributes of all `Atoms`.""",
|
|
837
|
+
)
|
|
838
|
+
|
|
839
|
+
B12 = _linkAtomAttribute(
|
|
840
|
+
"B12",
|
|
841
|
+
"""Array of `B12` elements of the Debye-Waller displacement tensors.
|
|
842
|
+
Assignment updates the U and anisotropy attributes of all `Atoms`.""",
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
B13 = _linkAtomAttribute(
|
|
846
|
+
"B13",
|
|
847
|
+
"""Array of `B13` elements of the Debye-Waller displacement tensors.
|
|
848
|
+
Assignment updates the U and anisotropy attributes of all `Atoms`.""",
|
|
849
|
+
)
|
|
850
|
+
|
|
851
|
+
B23 = _linkAtomAttribute(
|
|
852
|
+
"B23",
|
|
853
|
+
"""Array of `B23` elements of the Debye-Waller displacement tensors.
|
|
854
|
+
Assignment updates the U and anisotropy attributes of all `Atoms`.""",
|
|
855
|
+
)
|
|
856
|
+
|
|
857
|
+
# Private Methods --------------------------------------------------------
|
|
858
|
+
|
|
859
|
+
def __emptySharedStructure(self):
|
|
860
|
+
"""Return empty `Structure` with standard attributes same as in self."""
|
|
861
|
+
rv = Structure()
|
|
862
|
+
rv.__dict__.update([(k, getattr(self, k)) for k in rv.__dict__])
|
|
863
|
+
return rv
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
# End of class Structure
|