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,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