Source code for diffpy.structure.structure

#!/usr/bin/env python
##############################################################################
#
# diffpy.structure  by DANSE Diffraction group
#                   Simon J. L. Billinge
#                   (c) 2007 trustees of the Michigan State University.
#                   All rights reserved.
#
# File coded by:    Pavol Juhas
#
# See AUTHORS.txt for a list of people who contributed.
# See LICENSE_DANSE.txt for license information.
#
##############################################################################
"""This module defines class `Structure`."""

import copy as copymod
import warnings

import numpy

from diffpy.structure.atom import Atom
from diffpy.structure.lattice import Lattice
from diffpy.structure.utils import _link_atom_attribute, atom_bare_symbol, isiterable
from diffpy.utils._deprecator import build_deprecation_message, deprecated

# ----------------------------------------------------------------------------

base = "diffpy.structure.Structure"
removal_version = "4.0.0"
assignUniqueLabels_deprecation_msg = build_deprecation_message(
    base,
    "assignUniqueLabels",
    "assign_unique_labels",
    removal_version,
)
addNewAtom_deprecation_msg = build_deprecation_message(
    base,
    "addNewAtom",
    "add_new_atom",
    removal_version,
)
getLastAtom_deprecation_msg = build_deprecation_message(
    base,
    "getLastAtom",
    "get_last_atom",
    removal_version,
)
placeInLattice_deprecation_msg = build_deprecation_message(
    base,
    "placeInLattice",
    "place_in_lattice",
    removal_version,
)
readStr_deprecation_msg = build_deprecation_message(
    base,
    "readStr",
    "read_structure",
    removal_version,
)
writeStr_deprecation_msg = build_deprecation_message(
    base,
    "writeStr",
    "write_structure",
    removal_version,
)


[docs] class Structure(list): """Define group of atoms in a specified lattice. Structure --> group of atoms. `Structure` class is inherited from Python `list`. It contains a list of `Atom` instances. `Structure` overloads `setitem` and `setslice` methods so that the `lattice` attribute of atoms get set to `lattice`. Parameters ---------- atoms : list of Atom or Structure, Optional The list of `Atom` instances to be included in this `Structure`. When `atoms` argument is an existing `Structure` instance, the new structure is its copy. lattice : Lattice, Optional The instance of `Lattice` defining coordinate systems, property. title : str, Optional The string description of the structure. filename : str, Optional The name of a file to load the structure from. format : str, Optional The `Structure` format of the loaded `filename`. By default all structure formats are tried one by one. Ignored when `filename` has not been specified. Note ---- Cannot use `filename` and `atoms` arguments together. Overrides `atoms` argument when `filename` is specified. Attributes ---------- title : str The string description of the structure, default "". lattice : Lattice The instance of `Lattice` defining coordinate systems. pdffit : None or dict The dictionary of PDFFit-related metadata, default None. element : ndarray of str The character array of `Atom` types. Assignment updates the element attribute of the respective `Atoms`. Set the maximum length of the element string to 5 characters. xyz : ndarray The array of fractional coordinates of all `Atoms`. Assignment updates `xyz` attribute of all `Atoms` x : ndarray The array of fractional coordinate `x`. Assignment updates the `xyz` attribute of all `Atoms`. y : ndarray The array of fractional coordinate `y`. Assignment updates the `xyz` attribute of all `Atoms`. z : ndarray The array of fractional coordinate `z`. Assignment updates the `xyz` attribute of all `Atoms`. label : ndarray of str The character array of `Atom` names. Assignment updates the label attribute of all `Atoms`. Set the maximum length of the label string to 5 characters. occupancy : ndarray The array of `Atom` occupancies. Assignment updates the occupancy attribute of all `Atoms`. xyz_cartn : ndarray The array of absolute Cartesian coordinates of all `Atoms`. Assignment updates the `xyz` attribute of all `Atoms`. anisotropy : ndarray of bool The boolean array for anisotropic thermal displacement flags. Assignment updates the anisotropy attribute of all `Atoms`. U : ndarray The array of anisotropic thermal displacement tensors. Assignment updates the U and anisotropy attributes of all `Atoms`. Uisoequiv : ndarray The array of isotropic thermal displacement or equivalent values. Assignment updates the U attribute of all `Atoms`. U11 : ndarray The array of `U11` elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all `Atoms`. U22 : ndarray The array of `U22` elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all `Atoms`. U33 : ndarray The array of `U33` elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all `Atoms`. U12 : ndarray The array of `U12` elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all `Atoms`. U13 : ndarray The array of `U13` elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all `Atoms`. U23 : ndarray The array of `U23` elements of the anisotropic displacement tensors. Assignment updates the U and anisotropy attributes of all `Atoms`. Bisoequiv : ndarray The array of Debye-Waller isotropic thermal displacement or equivalent values for all `Atoms`. Assignment updates the `U` attribute of all `Atoms`. B11 : ndarray The array of `B11` elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all `Atoms`. B22 : ndarray The array of `B22` elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all `Atoms`. B33 : ndarray The array of `B33` elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all `Atoms`. B12 : ndarray The array of `B12` elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all `Atoms`. B13 : ndarray The array of `B13` elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all `Atoms`. B23 : ndarray The array of `B23` elements of the Debye-Waller displacement tensors. Assignment updates the U and anisotropy attributes of all `Atoms`. Examples -------- ``Structure(stru)`` create a copy of `Structure` instance stru. >>> stru = Structure() >>> copystru = Structure(stru) `Structure` is inherited from a list it can use list expansions. >>> oxygen_atoms = [a for a in stru if a.element == "O" ] >>> oxygen_stru = Structure(oxygen_atoms, lattice=stru.lattice) """ # default values for instance attributes title = "" _lattice = None pdffit = None def __init__(self, atoms=None, lattice=None, title=None, filename=None, format=None): # if filename is specified load it and return if filename is not None: if any((atoms, lattice, title)): emsg = "Cannot use filename and atoms arguments together." raise ValueError(emsg) readkwargs = (format is not None) and {"format": format} or {} self.read(filename, **readkwargs) return # copy initialization, must be first to allow lattice, title override if isinstance(atoms, Structure): Structure.__copy__(atoms, self) # assign arguments: if title is not None: self.title = title if lattice is not None: self.lattice = lattice elif self.lattice is None: self.lattice = Lattice() # insert atoms unless already done by __copy__ if not len(self) and atoms is not None: self.extend(atoms) return
[docs] def copy(self): """Return a copy of this `Structure` object.""" return copymod.copy(self)
def __copy__(self, target=None): """Create a deep copy of this instance. Parameters ---------- target : Optional target instance for copying, useful for copying a derived class. Defaults to new instance of the same type as self. Returns ------- A duplicate instance of this object. """ if target is None: target = Structure() elif target is self: return target # copy attributes as appropriate: target.title = self.title target.lattice = Lattice(self.lattice) target.pdffit = copymod.deepcopy(self.pdffit) # copy all atoms to the target target[:] = self return target def __str__(self): """Simple string representation.""" s_lattice = "lattice=%s" % self.lattice s_atoms = "\n".join([str(a) for a in self]) return s_lattice + "\n" + s_atoms @deprecated(addNewAtom_deprecation_msg) def addNewAtom(self, *args, **kwargs): """This function has been deprecated and will be removed in version 4.0.0. Please use diffpy.structure.Structure.add_new_atom instead. """ self.add_new_atom(*args, **kwargs) return
[docs] def add_new_atom(self, *args, **kwargs): """Add new `Atom` instance to the end of this `Structure`. Parameters ---------- *args, **kwargs : See `Atom` class constructor. Raises ------ UserWarning If an atom with the same element/type and coordinates already exists. """ kwargs["lattice"] = self.lattice atom = Atom(*args, **kwargs) for existing in self: if existing.element == atom.element and numpy.allclose(existing.xyz, atom.xyz): warnings.warn( f"Duplicate atom {atom.element} already exists at {atom.xyz!r}", category=UserWarning, stacklevel=2, ) break self.append(atom, copy=False) return
@deprecated(getLastAtom_deprecation_msg) def getLastAtom(self): """This function has been deprecated and will be removed in version 4.0.0. Please use diffpy.structure.Structure.get_last_atom instead. """ return self.get_last_atom()
[docs] def get_last_atom(self): """Return Reference to the last `Atom` in this structure.""" last_atom = self[-1] return last_atom
[docs] def get_chemical_symbols(self): """Return list of chemical symbols for all `Atoms` in this structure. Returns ------- list of str The list of chemical symbols for all `Atoms` in this structure. """ symbols_with_charge = [a.element for a in self] symbols = [atom_bare_symbol(sym) for sym in symbols_with_charge] return symbols
[docs] def get_fractional_coordinates(self): """Return array of fractional coordinates of all `Atoms` in this structure. Returns ------- numpy.ndarray The array of fractional coordinates of all `Atoms` in this structure in the same order as `Structure.get_chemical_symbols()`. """ coords = numpy.array([a.xyz for a in self]) return coords
[docs] def get_cartesian_coordinates(self): """Return array of Cartesian coordinates of all `Atoms` in this structure. Returns ------- numpy.ndarray The array of Cartesian coordinates of all `Atoms` in this structure in the same order as `Structure.get_chemical_symbols()`. """ cartn_coords = numpy.array([a.xyz_cartn for a in self]) return cartn_coords
[docs] def get_anisotropic_displacement_parameters(self, return_array=False): """Return the anisotropic displacement parameters for all atoms. Parameters ---------- return_array : bool, optional If True, return anisotropic displacement parameters as a numpy array instead of a dictionary. Returns ------- dict The dictionary of anisotropic displacement parameters for all atoms in this structure. Keys are of the form 'Element_i_Ujk', e.g. 'C_0_11', 'C_0_12'. """ if return_array: aniso_adps = numpy.array([a.U for a in self]) return aniso_adps else: adp_dict = {} for i, atom in enumerate(self): element = atom_bare_symbol(atom.element) adp_dict[f"{element}_{i}_11"] = self.U11[i] adp_dict[f"{element}_{i}_22"] = self.U22[i] adp_dict[f"{element}_{i}_33"] = self.U33[i] adp_dict[f"{element}_{i}_12"] = self.U12[i] adp_dict[f"{element}_{i}_13"] = self.U13[i] adp_dict[f"{element}_{i}_23"] = self.U23[i] return adp_dict
[docs] def get_isotropic_displacement_parameters(self, return_array=False): """Return a the isotropic displacement parameters for all atoms. Parameters ---------- return_array : bool, optional If True, return isotropic displacement parameters as a numpy array instead of a dictionary. Default is False. Returns ------- dict The dictionary of isotropic displacement parameters for all atoms in this structure. Keys are of the form 'Element_i_Uiso', e.g. 'C_0_Uiso'. """ if return_array: iso_adps = numpy.array([a.Uisoequiv for a in self]) return iso_adps else: iso_dict = {} for i, atom in enumerate(self): element = atom_bare_symbol(atom.element) iso_dict[f"{element}_{i+1}_Uiso"] = self.Uisoequiv[i] return iso_dict
[docs] def get_occupancies(self): """Return array of occupancies of all `Atoms` in this structure. Returns ------- numpy.ndarray The array of occupancies of all `Atoms` in this structure. """ occupancies = numpy.array([a.occupancy for a in self]) return occupancies
[docs] def get_lattice_vectors(self): """Return array of lattice vectors for this structure. Returns ------- numpy.ndarray The array of lattice vectors for this structure. """ lattice_vectors = self.lattice.base return lattice_vectors
[docs] def get_lattice_vector_angles(self): """Return array of lattice vector angles for this structure. Returns ------- numpy.ndarray The array of lattice vector angles for this structure. """ a, b, c = self.lattice.base alpha = self.lattice.angle(b, c) beta = self.lattice.angle(a, c) gamma = self.lattice.angle(a, b) return numpy.array([alpha, beta, gamma])
[docs] def assign_unique_labels(self): """Set a unique label string for each `Atom` in this structure. The label strings are formatted as "%(baresymbol)s%(index)i", where baresymbol is the element right-stripped of "[0-9][+-]". """ elnum = {} # support duplicate atom instances islabeled = set() for a in self: if a in islabeled: continue baresmbl = atom_bare_symbol(a.element) elnum[baresmbl] = elnum.get(baresmbl, 0) + 1 a.label = baresmbl + str(elnum[baresmbl]) islabeled.add(a) return
@deprecated(assignUniqueLabels_deprecation_msg) def assignUniqueLabels(self): """This function has been deprecated and will be removed in version 4.0.0. Please use diffpy.structure.Structure.assign_unique_labels instead. """ return self.assign_unique_labels()
[docs] def distance(self, aid0, aid1): """Calculate distance between 2 `Atoms`, no periodic boundary conditions. Parameters ---------- aid0 : int or str Zero based index of the first `Atom` or a string label. aid1 : int or str Zero based index or string label of the second atom. Returns ------- float Distance between the two `Atoms` in Angstroms. Raises ------ IndexError If any of the `Atom` indices or labels are invalid. """ # lookup by labels a0, a1 = self[aid0, aid1] return self.lattice.dist(a0.xyz, a1.xyz)
[docs] def angle(self, aid0, aid1, aid2): """The bond angle at the second of three `Atoms` in degrees. Parameters ---------- aid0 : int or str Zero based index of the first `Atom` or a string label. aid1 : int or str Index or string label for the second atom, where the angle is formed. aid2 : int or str Index or string label for the third atom. Returns ------- float The bond angle in degrees. Raises ------ IndexError If any of the arguments are invalid. """ a0, a1, a2 = self[aid0, aid1, aid2] u10 = a0.xyz - a1.xyz u12 = a2.xyz - a1.xyz return self.lattice.angle(u10, u12)
@deprecated(placeInLattice_deprecation_msg) def placeInLattice(self, new_lattice): """This function has been deprecated and will be removed in version 4.0.0. Please use diffpy.structure.Structure.place_in_lattice instead. """ return self.place_in_lattice(new_lattice)
[docs] def place_in_lattice(self, new_lattice): """Place structure into `new_lattice` coordinate system. Sets `lattice` to `new_lattice` and recalculate fractional coordinates of all `Atoms` so their absolute positions remain the same. Parameters ---------- new_lattice : Lattice New `lattice` to place the structure into. Returns ------- Structure Reference to this `Structure` object. The `lattice` attribute is updated to `new_lattice`. """ Tx = numpy.dot(self.lattice.base, new_lattice.recbase) Tu = numpy.dot(self.lattice.normbase, new_lattice.recnormbase) for a in self: a.xyz = numpy.dot(a.xyz, Tx) if a.anisotropy: a.U = numpy.dot(numpy.transpose(Tu), numpy.dot(a.U, Tu)) self.lattice = new_lattice return self
[docs] def read(self, filename, format="auto"): """Load structure from a file, any original data become lost. Parameters ---------- filename : str File to be loaded. format : str, Optional All structure formats are defined in parsers submodule, when ``format == 'auto'`` all parsers are tried one by one. Returns ------- Parser Return instance of data Parser used to process input string. This can be inspected for information related to particular format. """ import diffpy.structure import diffpy.structure.parsers get_parser = diffpy.structure.parsers.get_parser p = get_parser(format) new_structure = p.parse_file(filename) # reinitialize data after successful parsing # avoid calling __init__ from a derived class Structure.__init__(self) if new_structure is not None: self.__dict__.update(new_structure.__dict__) self[:] = new_structure if not self.title: import os.path tailname = os.path.basename(filename) tailbase = os.path.splitext(tailname)[0] self.title = tailbase return p
@deprecated(readStr_deprecation_msg) def readStr(self, s, format="auto"): """This function has been deprecated and will be removed in version 4.0.0. Please use diffpy.structure.Structure.read_structure instead. """ return self.read_structure(s, format)
[docs] def read_structure(self, s, format="auto"): """Read structure from a string. Parameters ---------- s : str String with structure definition. format : str, Optional All structure formats are defined in parsers submodule. When ``format == 'auto'``, all parsers are tried one by one. Returns ------- Parser Return instance of data Parser used to process input string. This can be inspected for information related to particular format. """ from diffpy.structure.parsers import get_parser p = get_parser(format) new_structure = p.parse(s) # reinitialize data after successful parsing # avoid calling __init__ from a derived class Structure.__init__(self) if new_structure is not None: self.__dict__.update(new_structure.__dict__) self[:] = new_structure return p
[docs] def write(self, filename, format): """Save structure to file in the specified format. Parameters ---------- filename : str File to save the structure to. format : str `Structure` format to use for saving. Note ---- Available structure formats can be obtained by: ``from parsers import formats`` """ from diffpy.structure.parsers import get_parser p = get_parser(format) p.filename = filename s = p.tostring(self) with open(filename, "w", encoding="utf-8", newline="") as fp: fp.write(s) return
@deprecated(writeStr_deprecation_msg) def writeStr(self, format): """This function has been deprecated and will be removed in version 4.0.0. Please use diffpy.structure.Structure.write_structure instead. """ return self.write_structure(format)
[docs] def write_structure(self, format): """Return string representation of the structure in specified format. Note ---- Available structure formats can be obtained by: ``from parsers import formats`` """ from diffpy.structure.parsers import get_parser p = get_parser(format) s = p.tostring(self) return s
[docs] def tolist(self): """Return `Atoms` in this `Structure` as a standard Python list.""" rv = [a for a in self] return rv
# Overloaded list Methods and Operators ----------------------------------
[docs] def append(self, a, copy=True): """Append `Atom` to a structure and update its `lattice` attribute. Parameters ---------- a : Atom Instance of `Atom` to be appended. copy : bool, Optional Flag for appending a copy of `a`. When ``False``, append `a` and update `a.lattice`. """ adup = copy and Atom(a) or a adup.lattice = self.lattice super(Structure, self).append(adup) return
[docs] def insert(self, idx, a, copy=True): """Insert `Atom` a before position idx in this `Structure`. Parameters ---------- idx : int Position in `Atom` list. a : Atom Instance of `Atom` to be inserted. copy : bool, Optional Flag for inserting a copy of `a`. When ``False``, append `a` and update `a.lattice`. """ adup = copy and copymod.copy(a) or a adup.lattice = self.lattice super(Structure, self).insert(idx, adup) return
[docs] def extend(self, atoms, copy=None): """Extend `Structure` with an iterable of `atoms`. Update the `lattice` attribute of all added `atoms`. Parameters ---------- atoms : Iterable The `Atom` objects to be appended to this `Structure`. copy : bool, Optional Flag for adding copies of `Atom` objects. Make copies when ``True``, append `atoms` unchanged when ``False``. The default behavior is to make copies when `atoms` are of `Structure` type or if new atoms introduce repeated objects. """ adups = (copymod.copy(a) for a in atoms) if copy is None: if isinstance(atoms, Structure): newatoms = adups else: memo = set(id(a) for a in self) def nextatom(a): return a if id(a) not in memo else copymod.copy(a) def mark(a): return (memo.add(id(a)), a)[-1] newatoms = (mark(nextatom(a)) for a in atoms) elif copy: newatoms = adups else: newatoms = atoms def setlat(a): return (setattr(a, "lattice", self.lattice), a)[-1] super(Structure, self).extend(setlat(a) for a in newatoms) return
def __getitem__(self, idx): """Get one or more `Atoms` in this structure. Parameters ---------- idx : int or str or Iterable `Atom` identifier. When integer use standard list lookup. For iterables use numpy lookup, this supports integer or boolean flag arrays. For string or string-containing iterables lookup the `Atoms` by string label. Returns ------- Atom or Structure An `Atom` instance for integer or string index or a substructure in all other cases. Raises ------ IndexError If the index is invalid or the `Atom` label is not unique. Examples -------- First `Atom` in the `Structure`: >>> stru[0] Substructure of all ``'Na'`` `Atoms`: >>> stru[stru.element == 'Na'] `Atom` with a unique label ``'Na3'``: >>> stru['Na3'] Substructure of three `Atoms`, lookup by label is more efficient when done for several `Atoms` at once. >>> stru['Na3', 2, 'Cl2'] """ if isinstance(idx, slice): rv = self.__empty_shared_structure() lst = super(Structure, self).__getitem__(idx) rv.extend(lst, copy=False) return rv try: rv = super(Structure, self).__getitem__(idx) return rv except TypeError: pass # check if there is any string label that should be resolved scalarstringlabel = isinstance(idx, str) hasstringlabel = scalarstringlabel or (isiterable(idx) and any(isinstance(ii, str) for ii in idx)) # if not, use numpy indexing to resolve idx if not hasstringlabel: idx1 = idx if type(idx) is tuple: idx1 = numpy.r_[idx] indices = numpy.arange(len(self))[idx1] rhs = [list.__getitem__(self, i) for i in indices] rv = self.__empty_shared_structure() rv.extend(rhs, copy=False) return rv # here we need to resolve at least one string label # build a map of labels to indices and mark duplicate labels duplicate = object() labeltoindex = {} for i, a in enumerate(self): labeltoindex[a.label] = duplicate if a.label in labeltoindex else i def _resolveindex(aid): aid1 = aid if type(aid) is str: aid1 = labeltoindex.get(aid, None) if aid1 is None: raise IndexError("Invalid atom label %r." % aid) if aid1 is duplicate: raise IndexError("Atom label %r is not unique." % aid) return aid1 # generate new index object that has no strings if scalarstringlabel: idx2 = _resolveindex(idx) # for iterables preserve the tuple object type else: idx2 = [_resolveindex(i) for i in idx] if type(idx) is tuple: idx2 = tuple(idx2) # call this function again and hope there is no recursion loop rv = self[idx2] return rv def __setitem__(self, idx, value, copy=True): """Assign `self[idx]` `Atom` to value. Parameters ---------- idx : int or slice Index of `Atom` in this `Structure` or a slice. value : Atom or Iterable Instance of `Atom` or an iterable. copy : bool, Optional Flag for making a copy of the value. When ``False``, update the `lattice` attribute of `Atom` objects present in value. Default is ``True``. """ # handle slice assignment if isinstance(idx, slice): def _fixlat(a): a.lattice = self.lattice return a v1 = value if copy: keep = set(super(Structure, self).__getitem__(idx)) v1 = (a if a in keep else Atom(a) for a in value) vfinal = filter(_fixlat, v1) # handle scalar assignment else: vfinal = Atom(value) if copy else value vfinal.lattice = self.lattice super(Structure, self).__setitem__(idx, vfinal) return def __add__(self, other): """Return new `Structure` object with appended `Atoms` from other. Parameters ---------- other : sequence of Atom Sequence of `Atom` instances. Returns ------- Structure New `Structure` with a copy of `Atom` instances. """ rv = copymod.copy(self) rv += other return rv def __iadd__(self, other): """Extend this `Structure` with `Atoms` from other. Parameters ---------- other : sequence of Atom Sequence of `Atom` instances. Returns ------- Structure Reference to this `Structure` object. """ self.extend(other, copy=True) return self def __sub__(self, other): """Return new `Structure` that has `Atoms` from the other removed. Parameters ---------- other : sequence of Atom Sequence of `Atom` instances. Returns ------- Structure New `Structure` with a copy of `Atom` instances. """ otherset = set(other) keepindices = [i for i, a in enumerate(self) if a not in otherset] rv = copymod.copy(self[keepindices]) return rv def __isub__(self, other): """Remove other `Atoms` if present in this structure. Parameters ---------- other : sequence of Atom Sequence of `Atom` instances. Returns ------- Structure Reference to this `Structure` object. """ otherset = set(other) self[:] = [a for a in self if a not in otherset] return self def __mul__(self, n): """Return new `Structure` with n-times concatenated `Atoms` from self. `Atoms` and `lattice` in the new structure are all copies. Parameters ---------- n : int Integer multiple. Returns ------- Structure New `Structure` with n-times concatenated `Atoms`. """ rv = copymod.copy(self[:0]) rv += n * self.tolist() return rv # right-side multiplication is the same as left-side __rmul__ = __mul__ def __imul__(self, n): """Concatenate this `Structure` to n-times more `Atoms`. For positive multiple the current `Atom` objects remain at the beginning of this `Structure`. Parameters ---------- n : int Integer multiple. Returns ------- Structure Reference to this `Structure` object. """ if n <= 0: self[:] = [] else: self.extend((n - 1) * self.tolist(), copy=True) return self # Properties ------------------------------------------------------------- # lattice def _get_lattice(self): return self._lattice def _set_lattice(self, value): for a in self: a.lattice = value self._lattice = value return lattice = property( _get_lattice, _set_lattice, doc="Coordinate system for this `Structure`.", ) # composition def _get_composition(self): rv = {} for a in self: rv[a.element] = rv.get(a.element, 0.0) + a.occupancy return rv composition = property( _get_composition, doc="Dictionary of chemical symbols and their total occupancies.", ) # linked atom attributes element = _link_atom_attribute( "element", ( "Character array of `Atom` types. Assignment updates " "the element attribute of the respective `Atoms`. " "Set the maximum length of the element string to 5 characters." ), toarray=lambda items: numpy.char.array(items, itemsize=5), ) xyz = _link_atom_attribute( "xyz", "Array of fractional coordinates of all `Atoms`. " "Assignment updates `xyz` attribute of all `Atoms`.", ) x = _link_atom_attribute( "x", "Array of all fractional coordinates `x`. " "Assignment updates `xyz` attribute of all `Atoms`.", ) y = _link_atom_attribute( "y", "Array of all fractional coordinates `y`. " "Assignment updates `xyz` attribute of all `Atoms`.", ) z = _link_atom_attribute( "z", "Array of all fractional coordinates `z`. " "Assignment updates `xyz` attribute of all `Atoms`.", ) label = _link_atom_attribute( "label", ( "Character array of `Atom` names. Assignment updates " "the label attribute of all `Atoms`. " "Set the maximum length of the label string to 5 characters." ), toarray=lambda items: numpy.char.array(items, itemsize=5), ) occupancy = _link_atom_attribute( "occupancy", "The `Atom` occupancies. Assignment updates the occupancy attributes of all `Atoms`" ) xyz_cartn = _link_atom_attribute( "xyz_cartn", "The `Atom` absolute Cartesian coordinates. Assignment updates the `xyz` attribute of all `Atoms`.", ) anisotropy = _link_atom_attribute( "anisotropy", " The anisotropic thermal displacement flags. Assignment updates the anisotropy attribute of all `Atoms`.", ) U = _link_atom_attribute( "U", "The anisotropic thermal displacement tensors. " "Assignment updates the U and anisotropy attributes of all `Atoms`.", ) Uisoequiv = _link_atom_attribute( "Uisoequiv", "The isotropic thermal displacement or equivalent values. " "Assignment updates the U attribute of all `Atoms`.", ) U11 = _link_atom_attribute( "U11", "The `U11` elements of the anisotropic displacement tensors. " "Assignment updates the U and anisotropy attributes of all `Atoms`.", ) U22 = _link_atom_attribute( "U22", "The `U22` elements of the anisotropic displacement tensors. " "Assignment updates the U and anisotropy attributes of all `Atoms`.", ) U33 = _link_atom_attribute( "U33", "The `U33` elements of the anisotropic displacement tensors. " "Assignment updates the U and anisotropy attributes of all `Atoms`.", ) U12 = _link_atom_attribute( "U12", "The `U12` elements of the anisotropic displacement tensors." "Assignment updates the U and anisotropy attributes of all `Atoms`.", ) U13 = _link_atom_attribute( "U13", "The `U13` elements of the anisotropic displacement tensors." "Assignment updates the U and anisotropy attributes of all `Atoms`.", ) U23 = _link_atom_attribute( "U23", "The `U23` elements of the anisotropic displacement tensors." "Assignment updates the U and anisotropy attributes of all `Atoms`.", ) Bisoequiv = _link_atom_attribute( "Bisoequiv", "The Debye-Waller isotropic thermal displacement or equivalent values." "Assignment updates the U attribute of all `Atoms`.", ) B11 = _link_atom_attribute( "B11", "The `B11` elements of the Debye-Waller displacement tensors. " "Assignment updates the U and anisotropy attributes of all `Atoms`.", ) B22 = _link_atom_attribute( "B22", "The `B22` elements of the Debye-Waller displacement tensors." "Assignment updates the U and anisotropy attributes of all `Atoms`.", ) B33 = _link_atom_attribute( "B33", "The `B33` elements of the Debye-Waller displacement tensors." "Assignment updates the U and anisotropy attributes of all `Atoms`.", ) B12 = _link_atom_attribute( "B12", "The `B12` elements of the Debye-Waller displacement tensors." "Assignment updates the U and anisotropy attributes of all `Atoms`.", ) B13 = _link_atom_attribute( "B13", "The `B13` elements of the Debye-Waller displacement tensors." "Assignment updates the U and anisotropy attributes of all `Atoms`.", ) B23 = _link_atom_attribute( "B23", "The `B23` elements of the Debye-Waller displacement tensors." "Assignment updates the U and anisotropy attributes of all `Atoms`.", ) # Private Methods -------------------------------------------------------- def __empty_shared_structure(self): """Return empty `Structure` with standard attributes same as in self.""" rv = Structure() rv.__dict__.update([(k, getattr(self, k)) for k in rv.__dict__]) return rv
# End of class Structure