Source code for distortion

# -*- coding: utf-8 -*-
from __future__ import print_function

"""
.. module:: Common_structures_for_Perovskites.

.. moduleauthor:: Dawei Wang <dwang5@zoho.com>

This file takes care of R-3 related structures, which are even more comlpex
than Pnma related phases.

"""
from ase import Atoms
import numpy as np
from ase.data import reference_states as _refstate
from ase.utils import basestring
from functools import reduce

from puc import Puc
from glazer import decode_glazer, my_equal

from copy import deepcopy

# class Pero_factory:
[docs]class Distortion: """ The class sets distortion to perovskite structure. Basic Information: :param symbols: Symbols of ABO3. :param lattice_constant: Lattice constant of cubic ABO3. :param grid: Number of unit cells along the three axes. :param covera: The ratio of the length of unit cell along **c-** axis to **a-** axis, not the supercell. Parameters to set up the distortion: :param glazer: Glazer notation of distortion. :param omega: The angles of the rotation along the three axes. :param u: Magnitudes of the vibration in three axes. :param k_u: The pattern of shift. :param local_mode: Eigenvalues of vibration. """ def __init__(self, system={ 'symbols': ['Ba', 'Ti', 'O'], 'lattice_constant': 4.0, 'grid': (2, 2, 2), 'covera': 1.0 }, distort=None ): """ Input parameters to set up the desired distortion. Default omega is around z axis, with a magnitude of 0.1 a0 is the lattice constant in the pseudocubic strucutre. For now, the compitiblity check falls on the user. """ self.system = system self.int_basis = np.diag(self.system['grid']) self.basis_factor = 1.0 self.symbols = self.system['symbols'] self.covera = self.system['covera'] # Converts the natural basis back to the crystallographic basis self.inverse_matrix = np.linalg.inv(np.transpose(self.int_basis)) self.pucs = self.generate_pucs() a0 = self.system['lattice_constant'] self.lattice = [[a0, 0, 0], [0, a0, 0], [0, 0, a0 * self.covera]] self.cell = np.dot(self.int_basis, self.lattice) # set default distort if it is not set. if distort is None: self._distort = { 'glazer': 'a0a0a0', 'omega': (0.0, 0.0, 0.0), 'u': (0.00, 0.0, 0.0), 'k_u': [[0, 0, 0], [0, 0, 0], [0, 0, 0]], 'local_mode': [0.00, 0.00, 0.00, 0.00, 0.00], } @property def distort(self): # Do something if you want return self._distort @distort.setter def distort(self, val): # Do something if you want self._distort = val # Since the distortion is changed, # I need to reset the pucs and the parameters. self.pucs = self.generate_pucs() self.set_parameters() def generate_pucs(self): a = self.int_basis[0][0] b = self.int_basis[1][1] c = self.int_basis[2][2] # set up the primitive unit cells. lg = [] # undistorted puc for i in range(a): for j in range(b): for k in range(c): puc = Puc(symbols=self.symbols, shift_index=[i, j, k]) lg.append(puc) return lg def get_atoms(self): # return atomic information to ASE. self.actuate_distort() return Atoms( symbols=self.element_names(), scaled_positions=self.bravais_basis(), cell=self.cell, pbc=True ) def set_parameters(self): # Deal with oxygen tilting first. self.omega = self._distort['omega'] relat2 = [ my_equal(self.omega[0], self.omega[1]), my_equal(self.omega[1], self.omega[2]), my_equal(self.omega[2], self.omega[0]) ] self.glazer = decode_glazer(self._distort['glazer']) self.k_omega = self.glazer[0] # the angles of rotation should be consistent with the letters of glazer notation if (self.glazer[1] != relat2): print("Given omega values are not consistent with the given Glazer notation.") exit() # Deal with ions displacements. self.k_u = self._distort['k_u'] self.local_mode = self._distort['local_mode'] self.u = self._distort['u'] def actuate_distort(self): if self._distort is not None: # Carry out the oxygen octahedron rotation and the displacements. self.rotate() self.shift()
[docs] def rotate_puc(self, puc): """ Rotate the ions in one primitive unit cell. """ sign = (-1) ** np.dot(self.k_omega, puc.shift_index) omega1 = sign * self.omega puc.rotate(omega1, self.covera)
def rotate(self): # Rotate the ions in all unit cell. list(map(lambda x: self.rotate_puc(x), self.pucs))
[docs] def shift_puc(self, puc): """ Move the ions in one primitive unit cell. """ sign = (-1) ** np.dot(self.k_u, puc.shift_index) u1 = sign * self.u puc.shift(u1, self.local_mode)
def shift(self): # Move the ions in all unit cell. list(map(lambda x: self.shift_puc(x), self.pucs)) def print(self): list(map(lambda x: x.print_atoms(), self.pucs)) print("---") def bravais_basis(self): #return absolute position p = list(map(lambda x: x.bravais_basis(), self.pucs)) q = reduce(lambda x, y: x + y, p) return list(map(lambda x: np.matmul(self.inverse_matrix, x), q)) def element_basis(self): #match atoms symbols p = list(map(lambda x: x.element_basis(), self.pucs)) p = reduce(lambda x, y: x + y, p) d = dict() count = 0 for i in p: if i in d: continue else: d[i] = count count += 1 return list(map(lambda x: d[x], p)) def element_names(self): #return atoms symbols p = list(map(lambda x: x.element_basis(), self.pucs)) p = reduce(lambda x, y: x + y, p) return p