Source code for dynasor.modes.atoms

import numpy as np

from ase.units import fs
from ase import Atoms

from .tools import inv
from ..units import Dalton_to_dmu


class DynasorAtoms:
    """Dynasor's representation of a structure"""
    def __init__(self, atoms: Atoms):
        """Initialized using an ASE Atoms object"""
        self._atoms = atoms

    @property
    def pos(self):
        """Cartesian positions"""
        return self._atoms.positions.copy()

    @property
    def positions(self):
        """Cartesian positions"""
        return self.pos

    @property
    def spos(self):
        """Reduced (or scaled) positions of atoms"""
        return self._atoms.get_scaled_positions()

    @property
    def scaled_positions(self):
        """Reduced (or scaled) positions of atoms"""
        return self.spos

    @property
    def cell(self):
        """Cell of atoms with cell vectors as rows"""
        return self._atoms.cell.array.copy()

    @property
    def inv_cell(self):
        """The inverse cell transpose so the inverse cell vectors are rows, no 2pi"""
        return np.linalg.inv(self._atoms.cell.array).T

    @property
    def numbers(self):
        """Chemical number for each atom, e.g. 1 for H, 2 for He etc."""
        return self._atoms.numbers.copy()

    @property
    def masses(self):
        """Masses of atoms in dmu"""
        return self._atoms.get_masses() / fs**2  # In eVfs²/Ų

    @property
    def volume(self):
        """Volume of cell"""
        return self._atoms.get_volume()

    @property
    def n_atoms(self):
        """Number of atoms"""
        return len(self._atoms)

    @property
    def symbols(self):
        """List of chemical symbol for each element"""
        return list(self._atoms.symbols)

    def to_ase(self):
        """Converts the internal Atoms to ASE Atoms"""
        return Atoms(cell=self.cell, numbers=self.numbers, positions=self.positions, pbc=True)

    def __repr__(self):
        return str(self)


[docs]class Prim(DynasorAtoms): def __str__(self): strings = [f"""Primitive cell: Number of atoms: {self.n_atoms} Volume: {self.volume:.3f} Atomic species present: {set(self.symbols)} Atomic numbers present: {set(self.numbers)} Cell: [[{self.cell[0, 0]:<20}, {self.cell[0, 1]:<20}, {self.cell[0, 2]:<20}], [{self.cell[1, 0]:<20}, {self.cell[1, 1]:<20}, {self.cell[1, 2]:<20}], [{self.cell[2, 0]:<20}, {self.cell[2, 1]:<20}, {self.cell[2, 2]:<20}]] """] strings.append(f"{'Ind':<5}{'Sym':<5}{'Num':<5}{'Mass (Da)':<10}{'x':<10}{'y':<10}{'z':<10}" f"{'a':<10}{'b':<10}{'c':<10}") atom_s = [] for i, p, sp, m, n, s in zip( range(self.n_atoms), self.positions, self.spos, self.masses / Dalton_to_dmu, self.numbers, [a.symbol for a in self.to_ase()]): atom_s.append(f'{i:<5}{s:<5}{n:<5}{m:<10.2f}{p[0]:<10.3f}{p[1]:<10.3f}{p[2]:<10.3f}' f'{sp[0]:<10.3f}{sp[1]:<10.3f}{sp[2]:<10.3f}') strings = strings + atom_s string = '\n'.join(strings) return string
[docs]class Supercell(DynasorAtoms): """The supercell takes care of some mappings between the primitive and repeated structure In particular the P-matrix connecting the cells as well as the offset-index of each atom is calculated. Note that the positions can not be revovered as offset x cell + basis since the atoms gets wrapped. Parameters ---------- supercell some ideal repetition of the prim cell and possible wrapping as either ASEAtoms prim primitive cell as either ASEAtoms """ def __init__(self, supercell, prim): self.prim = Prim(prim.copy()) super().__init__(supercell) # determine P-matrix relating supercell to primitive cell from dynasor.tools.structures import get_P_matrix self._P = get_P_matrix(self.prim.cell, self.cell) # P C = S self._P_inv = inv(self.P) # find the index and offsets for supercell using primitive as base unit from dynasor.tools.structures import get_offset_index self._offsets, self._indices = get_offset_index(prim, supercell, wrap=True) @property def P(self): """P-matrix is defined as dot(P, prim.cell) = supercell.cell""" return self._P.copy() @property def P_inv(self): """Inverse of P""" return self._P_inv.copy() @property def offsets(self): """The offset of each atom""" return self._offsets.copy() @property def indices(self): """The basis index of each atom""" return self._indices.copy() @property def n_cells(self): """Number of unit cells""" return self.n_atoms // self.prim.n_atoms def __str__(self): string = f"""Supercell: Number of atoms: {self.n_atoms} Volume: {self.volume:.3f} Number of unit cells: {self.n_cells} Cell: [[{self.cell[0, 0]:<20}, {self.cell[0, 1]:<20}, {self.cell[0, 2]:<20}], [{self.cell[1, 0]:<20}, {self.cell[1, 1]:<20}, {self.cell[1, 2]:<20}], [{self.cell[2, 0]:<20}, {self.cell[2, 1]:<20}, {self.cell[2, 2]:<20}]] P-matrix: [[{self.P[0, 0]:<20}, {self.P[0, 1]:<20}, {self.P[0, 2]:<20}], [{self.P[1, 0]:<20}, {self.P[1, 1]:<20}, {self.P[1, 2]:<20}], [{self.P[2, 0]:<20}, {self.P[2, 1]:<20}, {self.P[2, 2]:<20}]] {self.prim} """ return string