init
This commit is contained in:
528
electrochemistry/echem/eltransfer/GerischerMarkus.py
Normal file
528
electrochemistry/echem/eltransfer/GerischerMarkus.py
Normal file
@@ -0,0 +1,528 @@
|
||||
import numpy as np
|
||||
import scipy.integrate as integrate
|
||||
from scipy.optimize import minimize
|
||||
import numbers
|
||||
import typing
|
||||
from tqdm import tqdm
|
||||
from echem.core.useful_funcs import nearest_array_index, ClassMethods
|
||||
E_F_SHE_VAC = -4.5 # Fermi Energy of Standard Hydrogen Electrode with respect to vacuum
|
||||
|
||||
|
||||
class GM(ClassMethods):
|
||||
"""This class calculates the final Fermi and Redox species distributions according
|
||||
to the Gerischer-Marcus formalism.
|
||||
|
||||
Parameters:
|
||||
-----------
|
||||
DOS: np.ndarray, optional
|
||||
The values of DOS in 1D numpy array. If not specified values will be taken from saved data.
|
||||
|
||||
E: np.ndarray, optional
|
||||
The corresponding to the DOS energy mesh. If not specified values will be taken from saved data.
|
||||
|
||||
efermi: np.ndarray. optional
|
||||
System Fermi level. If not specified values will be taken from saved data.
|
||||
|
||||
vacuum_lvl: np.ndarray, optional
|
||||
System vacuum level. If not specified values will be taken from saved data.
|
||||
"""
|
||||
def __init__(self, path_to_data='Saved_data', DOS=None, E=None, efermi=None, vacuum_lvl=None):
|
||||
# variables that might be defined through __init__ function
|
||||
self.E = E
|
||||
self.DOS = DOS
|
||||
self.efermi = efermi
|
||||
self.vacuum_lvl = vacuum_lvl
|
||||
|
||||
# variables that should be defined through set_params function
|
||||
self.C_EDL = None
|
||||
self.T = None
|
||||
self.l = None
|
||||
self.sheet_area = None
|
||||
|
||||
# variables that will be created during calculations
|
||||
self.sigma_Q_arr = None
|
||||
|
||||
# variable that define numerical parameters of quantum charge calculation
|
||||
self.__SIGMA_0 = 0.5
|
||||
self.__SIGMA_ACCURACY = 1e-3
|
||||
self.__SIGMA_RANGE = 4
|
||||
|
||||
if DOS is None:
|
||||
try:
|
||||
self.DOS = np.load(path_to_data + '/DOS.npy')
|
||||
except OSError:
|
||||
print('File DOS.npy does not exist')
|
||||
|
||||
if E is None:
|
||||
try:
|
||||
self.E = np.load(path_to_data + '/E.npy')
|
||||
except OSError:
|
||||
print('File E_DOS.npy does not exist')
|
||||
|
||||
if efermi is None:
|
||||
try:
|
||||
self.efermi = np.load(path_to_data + '/efermi.npy')
|
||||
except OSError:
|
||||
print('File efermi.npy does not exist')
|
||||
|
||||
if vacuum_lvl is None:
|
||||
try:
|
||||
self.vacuum_lvl = np.load(path_to_data + '/vacuum_lvl.npy')
|
||||
except OSError:
|
||||
print('File vacuum_lvl.npy does not exist')
|
||||
|
||||
def set_params(self, C_EDL, T, l, sheet_area):
|
||||
"""Sets parameters of calculation
|
||||
|
||||
Parameters:
|
||||
----------
|
||||
C_EDL: float, str
|
||||
float: Capacitance of electric double layer (microF/cm^2)
|
||||
str: 'Q' calculating in the Quantum Capacitance Dominating limit (C_Q << C_EDL)
|
||||
str: 'Cl' calculating in the Classical limit (C_Q >> C_EDL)
|
||||
|
||||
T: int, float
|
||||
Temperature. It is used in computing Fermi function and distribution function of redox system states
|
||||
|
||||
l: float
|
||||
Reorganization energy in eV
|
||||
"""
|
||||
self.C_EDL = C_EDL
|
||||
self.T = T
|
||||
self.l = l
|
||||
|
||||
self.sheet_area = sheet_area
|
||||
|
||||
def set_params_advance(self, SIGMA_0=0.5, ACCURACY_SIGMA=1e-3, SIGMA_RANGE=4):
|
||||
"""
|
||||
Sets numerical parameters that are used in quantum charge density calculations. Delete cashed
|
||||
results of charge calculations.
|
||||
Args:
|
||||
SIGMA_0: float, optional
|
||||
Initial guess for charge at equilibrium
|
||||
ACCURACY_SIGMA: float, optional
|
||||
Accuracy of charge calculation
|
||||
SIGMA_RANGE: float, optional
|
||||
It defines the minimum and maximum calculated charge
|
||||
"""
|
||||
self.__SIGMA_0 = SIGMA_0
|
||||
self.__SIGMA_ACCURACY = ACCURACY_SIGMA
|
||||
self.__SIGMA_RANGE = SIGMA_RANGE
|
||||
self.sigma_Q_arr = None
|
||||
|
||||
@staticmethod
|
||||
def fermi_func(E, T):
|
||||
"""
|
||||
Calculates Fermi-Dirac Distribution
|
||||
Args:
|
||||
E: Energies
|
||||
T: Temperature in K
|
||||
"""
|
||||
k = 8.617e-5 # eV/K
|
||||
return 1 / (1 + np.exp(E / (k * T)))
|
||||
|
||||
@staticmethod
|
||||
def W_ox(E, T, l):
|
||||
"""
|
||||
Distribution of oxidized states
|
||||
Args:
|
||||
E (np.array): Energies
|
||||
T (float): Temperature
|
||||
l (float): Reorganization energy
|
||||
"""
|
||||
k = 8.617e-5 # eV/K
|
||||
W_0 = (1 / np.sqrt(4 * k * T * l))
|
||||
return W_0 * np.exp(- (E - l) ** 2 / (4 * k * T * l))
|
||||
|
||||
@staticmethod
|
||||
def W_red(E, T, l):
|
||||
"""
|
||||
Distribution of reduced states
|
||||
Args:
|
||||
E (np.array): Energies
|
||||
T (float): Temperature
|
||||
l (float): Reorganization energy
|
||||
"""
|
||||
k = 8.617e-5 # eV/K
|
||||
W_0 = (1 / np.sqrt(4 * k * T * l))
|
||||
return W_0 * np.exp(- (E + l) ** 2 / (4 * k * T * l))
|
||||
|
||||
def compute_C_quantum(self, dE_Q_arr):
|
||||
"""
|
||||
Calculates differential quantum capacitance
|
||||
Q = e * int{DOS(E) * [f(E) - f(E + deltaE)] dE}
|
||||
C_Q = - dQ/d(deltaE) = - (e / (4*k*T)) * int{DOS(E) * sech^2[(E+deltaE)/(2*k*T)] dE}
|
||||
Args:
|
||||
dE_Q_arr (np.array, float): Energy shift at which C_Q is calculated
|
||||
Returns:
|
||||
Quantum capacitance in accordance with energy displacement(s)
|
||||
TODO check constants
|
||||
"""
|
||||
self.check_existence('T')
|
||||
self.check_existence('sheet_area')
|
||||
|
||||
k = 8.617e-5 # eV/K
|
||||
|
||||
elementary_charge = 1.6e-19 # C
|
||||
k_1 = 1.38e-23 # J/K
|
||||
const = (1e6 * elementary_charge ** 2) / (4 * k_1 * self.sheet_area) # micro F / cm^2
|
||||
|
||||
if isinstance(dE_Q_arr, typing.Iterable):
|
||||
|
||||
C_q_arr = np.zeros_like(dE_Q_arr)
|
||||
|
||||
for i, dE_Q in enumerate(dE_Q_arr):
|
||||
E_2 = self.E - dE_Q # energy range for cosh function
|
||||
cosh = np.cosh(E_2 / (2 * k * self.T))
|
||||
integrand = (self.DOS / cosh) / cosh
|
||||
C_q = (const / self.T) * integrate.simps(integrand, self.E)
|
||||
C_q_arr[i] = C_q
|
||||
|
||||
return C_q_arr
|
||||
|
||||
def compute_C_total(self, E_diff_arr, add_info=False):
|
||||
|
||||
sigma_arr = np.zeros_like(E_diff_arr)
|
||||
for i, E_diff in tqdm(enumerate(E_diff_arr), total=len(E_diff_arr)):
|
||||
sigma_arr[i] = self.compute_sigma(E_diff, sigma_0=sigma_arr[i-1])
|
||||
|
||||
C_tot_arr = np.zeros_like(E_diff_arr)
|
||||
C_Q_arr = np.zeros_like(E_diff_arr)
|
||||
|
||||
for i, (E_diff, sigma) in enumerate(zip(E_diff_arr, sigma_arr)):
|
||||
ind = nearest_array_index(self.sigma_Q_arr, sigma)
|
||||
E_step = self.__SIGMA_ACCURACY
|
||||
E_start = - self.__SIGMA_RANGE
|
||||
dE_Q = E_start + E_step * ind
|
||||
C_Q = self.compute_C_quantum([dE_Q])
|
||||
C_Q_arr[i] = C_Q[0]
|
||||
C_tot = C_Q * self.C_EDL / (C_Q + self.C_EDL)
|
||||
C_tot_arr[i] = C_tot
|
||||
|
||||
if add_info is False:
|
||||
return C_tot_arr
|
||||
else:
|
||||
return C_tot_arr, C_Q_arr, sigma_arr
|
||||
|
||||
def compute_sigma_EDL(self, dE_EDL):
|
||||
"""
|
||||
Calculates charge corresponding to the potential drop of -dE_EDL/|e|.
|
||||
Takes into account integral capacitance C_EDL
|
||||
Args:
|
||||
dE_EDL (float, np.array): Electron energy shift due to potential drop
|
||||
Returns:
|
||||
Charge or Sequence of charges
|
||||
"""
|
||||
self.check_existence('C_EDL')
|
||||
return - self.C_EDL * dE_EDL
|
||||
|
||||
def compute_sigma_quantum(self, dE_Q_arr):
|
||||
"""
|
||||
Computes surface charge density induced by depletion or excess of electrons
|
||||
|
||||
Parameters:
|
||||
----------
|
||||
dE_Q_arr: np.ndarray, float
|
||||
Shift in Fermi level due to quantum capacitance
|
||||
|
||||
Returns:
|
||||
-------
|
||||
sigmas: np.ndarray, float
|
||||
Computed values (or one value) of surface charge densities
|
||||
"""
|
||||
|
||||
self.check_existence('T')
|
||||
self.check_existence('sheet_area')
|
||||
|
||||
elementary_charge = 1.6e-13 # micro coulomb
|
||||
|
||||
if isinstance(dE_Q_arr, typing.Iterable):
|
||||
y_fermi = self.fermi_func(self.E, self.T)
|
||||
|
||||
sigmas = []
|
||||
|
||||
for dE_Q in dE_Q_arr:
|
||||
E_2 = self.E - dE_Q # energy range for shifted Fermi_Dirac function
|
||||
y_fermi_shifted = self.fermi_func(E_2, self.T)
|
||||
integrand = self.DOS * (y_fermi - y_fermi_shifted)
|
||||
sigma = (elementary_charge / self.sheet_area) * integrate.simps(integrand, self.E)
|
||||
sigmas.append(sigma)
|
||||
|
||||
return sigmas
|
||||
|
||||
elif isinstance(dE_Q_arr, numbers.Real):
|
||||
y_fermi = self.fermi_func(self.E, self.T)
|
||||
|
||||
E_2 = self.E - dE_Q_arr # energy range for shifted Fermi_Dirac function
|
||||
y_fermi_shifted = self.fermi_func(E_2, self.T)
|
||||
integrand = self.DOS * (y_fermi - y_fermi_shifted)
|
||||
sigma = (elementary_charge / self.sheet_area) * integrate.simps(integrand, self.E)
|
||||
|
||||
return sigma
|
||||
else:
|
||||
raise TypeError(f'Invalid type of dE_Q_arr: {type(dE_Q_arr)}')
|
||||
|
||||
def compute_sigma(self, E_diff, sigma_0=None):
|
||||
|
||||
def error_E_diff(sigma, E_diff, sigma_Q_arr):
|
||||
ind = nearest_array_index(sigma_Q_arr, sigma)
|
||||
dE_Q = E_start + E_step * ind
|
||||
dE_EDL = - sigma / self.C_EDL
|
||||
dE_total = dE_Q + dE_EDL
|
||||
|
||||
return (dE_total - E_diff) ** 2
|
||||
|
||||
for var in ['T', 'l', 'C_EDL']:
|
||||
self.check_existence(var)
|
||||
|
||||
E_step = self.__SIGMA_ACCURACY
|
||||
E_start = - self.__SIGMA_RANGE
|
||||
if sigma_0 is None:
|
||||
sigma_0 = self.__SIGMA_0
|
||||
# check if we've already calculated sigma_Q_arr in another run
|
||||
if self.sigma_Q_arr is None:
|
||||
E_range = np.arange(E_start, -E_start, E_step)
|
||||
sigma_Q_arr = self.compute_sigma_quantum(E_range)
|
||||
self.sigma_Q_arr = sigma_Q_arr
|
||||
else:
|
||||
sigma_Q_arr = self.sigma_Q_arr
|
||||
|
||||
result = minimize(error_E_diff, np.array([sigma_0]), args=(E_diff, sigma_Q_arr))
|
||||
sigma = result.x[0]
|
||||
|
||||
return sigma
|
||||
|
||||
def compute_distributions(self, V_std, overpot=0, reverse=False, add_info=False):
|
||||
"""Computes Fermi-Dirac and Redox species distributions according to Gerischer-Markus formalism
|
||||
with Quantum Capacitance
|
||||
|
||||
Parameters:
|
||||
----------
|
||||
V_std: float
|
||||
Standard potential (vs SHE) of a redox couple (Volts)
|
||||
overpot: float, optional
|
||||
Overpotential (Volts). It shifts the electrode Fermi energy to -|e|*overpot
|
||||
reverse: bool, optional
|
||||
If reverse is False the process of electron transfer from electrode to the oxidized state of the
|
||||
redox species is considered and vice versa
|
||||
add_info: bool, optional
|
||||
If False the func returns Fermi-Dirac and Redox species distributions
|
||||
If True additionally returns dE_Q (Fermi energy shift due to the quantum capacitance),
|
||||
sigma (surface charge) and E_diff (the whole energy shift with respect to the original Fermi level)
|
||||
|
||||
Returns:
|
||||
-------
|
||||
y_fermi: np.array
|
||||
Fermi-Dirac distribution
|
||||
y_redox: np.array
|
||||
Redox species distributions
|
||||
dE_Q: np.array, optional (if add_info == True)
|
||||
Total shift of the Fermi energy due to the Quantum Capacitance
|
||||
sigma: np.array, optional (if add_info == True)
|
||||
surface charge in microF/cm^2
|
||||
E_F_redox: np.array, optional (if add_info == True)
|
||||
The sum of two energy displacement of the electrode due to the difference in Fermi level of Redox couple
|
||||
and the electrode and overpotential. It splits into dE_Q and dE_EDL
|
||||
"""
|
||||
|
||||
E_F_redox = E_F_SHE_VAC - self.efermi - V_std + self.vacuum_lvl - overpot
|
||||
sigma = self.compute_sigma(E_F_redox)
|
||||
|
||||
ind = nearest_array_index(self.sigma_Q_arr, sigma)
|
||||
E_step = self.__SIGMA_ACCURACY
|
||||
E_start = - self.__SIGMA_RANGE
|
||||
dE_Q = E_start + E_step * ind
|
||||
|
||||
E_fermi = self.E - dE_Q
|
||||
E_DOS_redox = self.E - dE_Q - overpot
|
||||
|
||||
if reverse:
|
||||
y_fermi = 1 - self.fermi_func(E_fermi, self.T)
|
||||
y_redox = self.W_red(E_DOS_redox, self.T, self.l)
|
||||
else:
|
||||
y_fermi = self.fermi_func(E_fermi, self.T)
|
||||
y_redox = self.W_ox(E_DOS_redox, self.T, self.l)
|
||||
|
||||
if not add_info:
|
||||
return y_fermi, y_redox
|
||||
else:
|
||||
return y_fermi, y_redox, dE_Q, sigma, E_F_redox
|
||||
|
||||
def compute_k_HET(self, V_std_pot_arr, overpot_arr, reverse=False, add_info=False):
|
||||
"""Computes integral k_HET using Gerischer-Markus formalism with quantum capacitance
|
||||
|
||||
Parameters:
|
||||
----------
|
||||
V_std_pot_arr: float, np.ndarray
|
||||
A range of varying a standard potential
|
||||
overpot_arr: float, np.ndarray
|
||||
A range of varying an overpotential
|
||||
reverse: bool, optional
|
||||
if reverse is False the process of electron transfer from electrode to the oxidized state of the
|
||||
redox mediator is considered and vice versa
|
||||
|
||||
Returns:
|
||||
-------
|
||||
k_HET: np.array
|
||||
Calculated heterogeneous electron transfer rate constant according to Gerischer-Marcus model with quantum
|
||||
capacitance
|
||||
dE_Q_arr: np.ndarray, optional (if add_info == True)
|
||||
Total shift of the Fermi energy due to the Quantum Capacitance for all calculated redox potentials or
|
||||
overpotentials
|
||||
sigma_arr: np.ndarray, optional (if add_info == True)
|
||||
surface charge in microF/cm^2 for all calculated redox potentials or overpotentials
|
||||
E_F_redox_arr: np.ndarray, optional (if add_info == True)
|
||||
The sum of two energy displacement of the electrode due to the difference in Fermi level of Redox couple
|
||||
and the electrode and overpotential. It splits into dE_Q and dE_EDL. For all calculated redox potentials
|
||||
or overpotentials
|
||||
y_fermi_arr: 2D np.ndarray, optional (if add_info == True)
|
||||
Fermi-Dirac distribution for all calculated redox potentials or overpotentials
|
||||
y_redox_arr: 2D np.ndarray, optional (if add_info == True)
|
||||
Redox species distributions for all calculated redox potentials or overpotentials
|
||||
"""
|
||||
|
||||
if isinstance(self.C_EDL, numbers.Real):
|
||||
if isinstance(V_std_pot_arr, typing.Iterable) and isinstance(overpot_arr, numbers.Real):
|
||||
k_HET = np.zeros_like(V_std_pot_arr)
|
||||
if not add_info:
|
||||
for i, V_std in tqdm(enumerate(V_std_pot_arr), total=len(V_std_pot_arr)):
|
||||
y_fermi, y_redox = self.compute_distributions(V_std, reverse=reverse, overpot=overpot_arr)
|
||||
integrand = self.DOS * y_fermi * y_redox
|
||||
k_HET[i] = integrate.simps(integrand, self.E) / self.sheet_area
|
||||
return k_HET
|
||||
else:
|
||||
dE_Q_arr = np.zeros_like(V_std_pot_arr)
|
||||
sigma_arr = np.zeros_like(V_std_pot_arr)
|
||||
E_F_redox_arr = np.zeros_like(V_std_pot_arr)
|
||||
y_fermi_arr = np.zeros((len(V_std_pot_arr), len(self.E)))
|
||||
y_redox_arr = np.zeros((len(V_std_pot_arr), len(self.E)))
|
||||
for i, V_std in tqdm(enumerate(V_std_pot_arr), total=len(V_std_pot_arr)):
|
||||
y_fermi, y_redox, dE_Q, sigma, E_F_redox = self.compute_distributions(V_std, reverse=reverse,
|
||||
overpot=overpot_arr,
|
||||
add_info=add_info)
|
||||
integrand = self.DOS * y_fermi * y_redox
|
||||
k_HET[i] = integrate.simps(integrand, self.E) / self.sheet_area
|
||||
dE_Q_arr[i] = dE_Q
|
||||
sigma_arr[i] = sigma
|
||||
E_F_redox_arr[i] = E_F_redox
|
||||
y_fermi_arr[i] = y_fermi
|
||||
y_redox_arr[i] = y_redox
|
||||
return k_HET, dE_Q_arr, sigma_arr, E_F_redox_arr, y_fermi_arr, y_redox_arr
|
||||
|
||||
elif isinstance(overpot_arr, typing.Iterable) and isinstance(V_std_pot_arr, numbers.Real):
|
||||
k_HET = np.zeros_like(overpot_arr)
|
||||
if not add_info:
|
||||
for i, overpot in tqdm(enumerate(overpot_arr), total=len(overpot_arr)):
|
||||
y_fermi, y_redox = self.compute_distributions(V_std_pot_arr, reverse=reverse, overpot=overpot)
|
||||
integrand = self.DOS * y_fermi * y_redox
|
||||
k_HET[i] = integrate.simps(integrand, self.E) / self.sheet_area
|
||||
|
||||
return k_HET
|
||||
else:
|
||||
dE_Q_arr = np.zeros_like(overpot_arr)
|
||||
sigma_arr = np.zeros_like(overpot_arr)
|
||||
E_F_redox_arr = np.zeros_like(overpot_arr)
|
||||
y_fermi_arr = np.zeros((len(overpot_arr), len(self.E)))
|
||||
y_redox_arr = np.zeros((len(overpot_arr), len(self.E)))
|
||||
for i, overpot in tqdm(enumerate(overpot_arr), total=len(overpot_arr)):
|
||||
y_fermi, y_redox, dE_Q, sigma, E_F_redox = self.compute_distributions(V_std_pot_arr,
|
||||
reverse=reverse,
|
||||
overpot=overpot,
|
||||
add_info=add_info)
|
||||
integrand = self.DOS * y_fermi * y_redox
|
||||
k_HET[i] = integrate.simps(integrand, self.E) / self.sheet_area
|
||||
dE_Q_arr[i] = dE_Q
|
||||
sigma_arr[i] = sigma
|
||||
E_F_redox_arr[i] = E_F_redox
|
||||
y_fermi_arr[i] = y_fermi
|
||||
y_redox_arr[i] = y_redox
|
||||
return k_HET, dE_Q_arr, sigma_arr, E_F_redox_arr, y_fermi_arr, y_redox_arr
|
||||
|
||||
else:
|
||||
raise ValueError('One and only one type of V_std_pot_arr and overpot arr must be Sequence. The other \
|
||||
must be a Real number')
|
||||
|
||||
elif self.C_EDL == 'Cl':
|
||||
if isinstance(V_std_pot_arr, typing.Iterable) and isinstance(overpot_arr, numbers.Real):
|
||||
E_fermi = self.E
|
||||
E_DOS_redox = self.E - overpot_arr
|
||||
|
||||
if reverse:
|
||||
y_fermi = 1 - self.fermi_func(E_fermi, self.T)
|
||||
y_redox = self.W_red(E_DOS_redox, self.T, self.l)
|
||||
else:
|
||||
y_fermi = self.fermi_func(E_fermi, self.T)
|
||||
y_redox = self.W_ox(E_DOS_redox, self.T, self.l)
|
||||
|
||||
integrand = self.DOS * y_fermi * y_redox
|
||||
k_HET = np.ones_like(V_std_pot_arr) * integrate.simps(integrand, self.E)
|
||||
|
||||
return k_HET
|
||||
|
||||
elif isinstance(overpot_arr, typing.Sequence) and isinstance(V_std_pot_arr, numbers.Real):
|
||||
k_HET = np.zeros_like(overpot_arr)
|
||||
|
||||
for i, overpot in tqdm(enumerate(overpot_arr), total=len(overpot_arr)):
|
||||
E_fermi = self.E
|
||||
E_DOS_redox = self.E - overpot
|
||||
|
||||
if reverse:
|
||||
y_fermi = 1 - self.fermi_func(E_fermi, self.T)
|
||||
y_redox = self.W_red(E_DOS_redox, self.T, self.l)
|
||||
else:
|
||||
y_fermi = self.fermi_func(E_fermi, self.T)
|
||||
y_redox = self.W_ox(E_DOS_redox, self.T, self.l)
|
||||
|
||||
integrand = self.DOS * y_fermi * y_redox
|
||||
k_HET[i] = integrate.simps(integrand, self.E)
|
||||
|
||||
return k_HET
|
||||
|
||||
else:
|
||||
raise ValueError('One and only one type of V_std_pot_arr and overpot arr must be Sequence. The other \
|
||||
must be Real number')
|
||||
|
||||
elif self.C_EDL == 'Q':
|
||||
if isinstance(V_std_pot_arr, typing.Iterable) and isinstance(overpot_arr, numbers.Real):
|
||||
k_HET = np.zeros_like(V_std_pot_arr)
|
||||
|
||||
for i, V_std in tqdm(enumerate(V_std_pot_arr), total=len(V_std_pot_arr)):
|
||||
E_F_redox = E_F_SHE_VAC - self.efermi - V_std + self.vacuum_lvl
|
||||
E_DOS_redox = self.E - E_F_redox
|
||||
E_fermi = E_DOS_redox - overpot_arr
|
||||
|
||||
if reverse:
|
||||
y_fermi = 1 - self.fermi_func(E_fermi, self.T)
|
||||
y_redox = self.W_red(E_DOS_redox, self.T, self.l)
|
||||
else:
|
||||
y_fermi = self.fermi_func(E_fermi, self.T)
|
||||
y_redox = self.W_ox(E_DOS_redox, self.T, self.l)
|
||||
|
||||
integrand = self.DOS * y_fermi * y_redox
|
||||
k_HET[i] = integrate.simps(integrand, self.E)
|
||||
|
||||
return k_HET
|
||||
|
||||
elif isinstance(overpot_arr, typing.Iterable) and isinstance(V_std_pot_arr, numbers.Real):
|
||||
k_HET = np.zeros_like(overpot_arr)
|
||||
|
||||
for i, overpot in tqdm(enumerate(overpot_arr), total=len(overpot_arr)):
|
||||
E_F_redox = E_F_SHE_VAC - self.efermi - V_std_pot_arr + self.vacuum_lvl - overpot
|
||||
E_fermi = self.E - E_F_redox
|
||||
E_DOS_redox = self.E - E_F_redox - overpot
|
||||
|
||||
if reverse:
|
||||
y_fermi = 1 - self.fermi_func(E_fermi, self.T)
|
||||
y_redox = self.W_red(E_DOS_redox, self.T, self.l)
|
||||
else:
|
||||
y_fermi = self.fermi_func(E_fermi, self.T)
|
||||
y_redox = self.W_ox(E_DOS_redox, self.T, self.l)
|
||||
|
||||
integrand = self.DOS * y_fermi * y_redox
|
||||
k_HET[i] = integrate.simps(integrand, self.E)
|
||||
|
||||
return k_HET
|
||||
|
||||
else:
|
||||
raise ValueError('One and only one type of V_std_pot_arr and overpot arr must be Sequence. The other \
|
||||
must be Real number')
|
||||
0
electrochemistry/echem/eltransfer/__init__.py
Normal file
0
electrochemistry/echem/eltransfer/__init__.py
Normal file
514
electrochemistry/echem/eltransfer/kHET_spatial.py
Normal file
514
electrochemistry/echem/eltransfer/kHET_spatial.py
Normal file
@@ -0,0 +1,514 @@
|
||||
import numpy as np
|
||||
from . import tip_types
|
||||
from .GerischerMarkus import GM
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.interpolate import griddata
|
||||
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
||||
from pymatgen.io.vasp import Procar
|
||||
from echem.core import constants
|
||||
from echem.io_data import vasp
|
||||
|
||||
|
||||
class kHET:
|
||||
"""
|
||||
This class calculates heterogeneous electron transfer rate constant with spatial resolution
|
||||
"""
|
||||
# TODO update tips types
|
||||
|
||||
AVAILABLE_TIPS_TYPES = ['oxygen', 'IrCl6', 'RuNH3_6', 'RuNH3_6_NNN_plane', 'RuNH3_6_perpendicular',
|
||||
'oxygen_parallel_x', 'oxygen_parallel_y']
|
||||
|
||||
def __init__(self, working_folder=''):
|
||||
if working_folder == '':
|
||||
working_folder = '.'
|
||||
# TODO think about better than get class objects for outcar and poscar info
|
||||
self.outcar = vasp.Outcar.from_file(working_folder + '/OUTCAR')
|
||||
self.poscar = vasp.Poscar.from_file(working_folder + '/POSCAR')
|
||||
self.procar = Procar(working_folder + '/PROCAR')
|
||||
self.working_folder = working_folder
|
||||
self.path_to_data = working_folder + '/Saved_data'
|
||||
self.wavecar = None
|
||||
|
||||
self.C_EDL = None
|
||||
self.T = None
|
||||
self.lambda_ = None
|
||||
self.sheet_area = None
|
||||
self.V_std = None
|
||||
self.overpot = None
|
||||
self.dE_Q = None
|
||||
self.kb_array = None
|
||||
self.E = None
|
||||
|
||||
def set_parameters(self, T, lambda_, overpot=0, V_std=None, C_EDL=None, dE_Q=None, linear_constant=26, threshold_value=1e-5, shifted_DOS='all'):
|
||||
"""
|
||||
:param C_EDL: float
|
||||
Capacitance of electric double layer (microF/cm^2)
|
||||
:param T: int, float
|
||||
Temperature. It is used in computing Fermi function and distribution function of redox system states
|
||||
:param lambda_: float
|
||||
Reorganization energy in eV
|
||||
:param V_std: float
|
||||
Standart potential of the redox couple (Volts)
|
||||
:param overpot: float
|
||||
Overpotential (Volts). It shifts the electrode Fermi energy to -|e|*overpot
|
||||
:return:
|
||||
"""
|
||||
#TODO check get_DOS parameters
|
||||
self.E, DOS = self.outcar.get_DOS(zero_at_fermi=True, smearing='Gaussian', sigma=0.1, dE = 0.01)
|
||||
self.T = T
|
||||
self.lambda_ = lambda_
|
||||
self.overpot = overpot
|
||||
self.sheet_area = self._get_sheet_area() # Area of investigated surface(XY) in cm^2
|
||||
self.linear_constant = linear_constant
|
||||
if V_std == None or C_EDL == None:
|
||||
if dE_Q == None:
|
||||
raise ValueError ("Set either V_std and C_EDL or dE_Q parameter")
|
||||
else:
|
||||
self.dE_Q = dE_Q
|
||||
else:
|
||||
self.V_std = V_std
|
||||
self.C_EDL = C_EDL
|
||||
self.dE_Q = self._calculate_dE_Q()
|
||||
if shifted_DOS == 'all':
|
||||
self.are_frozen_states = False
|
||||
self.kb_array = self._get_kb_array(threshold_value)
|
||||
else:
|
||||
self.are_frozen_states = True
|
||||
self.kb_array = self._get_kb_array(threshold_value, shifted_DOS)
|
||||
if self.kb_array == []:
|
||||
print('Error! kb_array is empty. Try to decrease threshold_value')
|
||||
|
||||
def load_wavecar(self):
|
||||
self.wavecar = vasp.Wavecar.from_file(self.working_folder + '/WAVECAR', self.kb_array)
|
||||
|
||||
def plot_distributions(self):
|
||||
#TODO make it
|
||||
pass
|
||||
|
||||
def _get_atom_localization(self, kpoint, band, target_atom_types):
|
||||
for key in self.procar.data.keys():
|
||||
procar_data = self.procar.data[key]
|
||||
list_of_ions = []
|
||||
atomnames = self.poscar.structure.species
|
||||
orbital_names = self.procar.orbitals
|
||||
for i, name in enumerate(atomnames):
|
||||
if name in target_atom_types:
|
||||
list_of_ions.append(i)
|
||||
list_of_orbitals = [i for i in range(len(orbital_names))]
|
||||
|
||||
localization = 0
|
||||
for ion in list_of_ions:
|
||||
for orb in list_of_orbitals:
|
||||
localization += procar_data[kpoint - 1][band - 1][ion][orb]
|
||||
return localization
|
||||
|
||||
def _get_kb_array(self, threshold_value, shifted_DOS='all', threshold_for_localization_to_be_shifted=0.4):
|
||||
if shifted_DOS=='all':
|
||||
fermi_distribution = GM.fermi_func(self.E - self.dE_Q, self.T)
|
||||
W_ox = GM.W_ox(self.E - self.dE_Q - self.overpot, self.T, self.lambda_)
|
||||
E_satisfy_mask = W_ox * fermi_distribution > np.max(W_ox) * np.max(fermi_distribution) * threshold_value
|
||||
list_of_E_indices = [i for i, E in enumerate(E_satisfy_mask) if E]
|
||||
min_E_ind = min(list_of_E_indices)
|
||||
max_E_ind = max(list_of_E_indices)
|
||||
Erange = [self.E[min_E_ind], self.E[max_E_ind]]
|
||||
|
||||
kb_array = []
|
||||
for band in range(1, self.outcar.nbands + 1):
|
||||
for kpoint in range(1, self.outcar.nkpts + 1):
|
||||
energy = self.outcar.eigenvalues[0][kpoint - 1][band - 1] - self.outcar.efermi
|
||||
if energy >= Erange[0] and energy < Erange[1]:
|
||||
kb_array.append([kpoint, band])
|
||||
else:
|
||||
fermi_distribution_shifted = GM.fermi_func(self.E - self.dE_Q, self.T)
|
||||
W_ox_shifted = GM.W_ox(self.E - self.dE_Q - self.overpot, self.T, self.lambda_)
|
||||
E_satisfy_mask_shifted = W_ox_shifted * fermi_distribution_shifted > np.max(W_ox_shifted) * np.max(
|
||||
fermi_distribution_shifted) * threshold_value
|
||||
list_of_E_indices_shifted = [i for i, E in enumerate(E_satisfy_mask_shifted) if E]
|
||||
min_E_ind_shifted = min(list_of_E_indices_shifted)
|
||||
max_E_ind_shifted = max(list_of_E_indices_shifted)
|
||||
Erange_shifted = [self.E[min_E_ind_shifted], self.E[max_E_ind_shifted]]
|
||||
|
||||
fermi_distribution_frozen = GM.fermi_func(self.E - 0, self.T)
|
||||
W_ox_frozen = GM.W_ox(self.E - 0 - self.overpot, self.T, self.lambda_)
|
||||
E_satisfy_mask_frozen = W_ox_frozen * fermi_distribution_frozen > np.max(W_ox_frozen) * np.max(
|
||||
fermi_distribution_frozen) * threshold_value
|
||||
list_of_E_indices_frozen = [i for i, E in enumerate(E_satisfy_mask_frozen) if E]
|
||||
min_E_ind_frozen = min(list_of_E_indices_frozen)
|
||||
max_E_ind_frozen = max(list_of_E_indices_frozen)
|
||||
Erange_frozen = [self.E[min_E_ind_frozen], self.E[max_E_ind_frozen]]
|
||||
|
||||
kb_array = []
|
||||
self.frozen_mask = []
|
||||
for band in range(1, self.outcar.nbands + 1):
|
||||
for kpoint in range(1, self.outcar.nkpts + 1):
|
||||
energy = self.outcar.eigenvalues[0][kpoint - 1][band - 1] - self.outcar.efermi
|
||||
if energy >= Erange_frozen[0] and energy < Erange_frozen[1]:
|
||||
if self._get_atom_localization(kpoint, band,
|
||||
target_atom_types=shifted_DOS) < threshold_for_localization_to_be_shifted:
|
||||
kb_array.append([kpoint, band])
|
||||
self.frozen_mask.append(1)
|
||||
if energy >= Erange_shifted[0] and energy < Erange_shifted[1]:
|
||||
if self._get_atom_localization(kpoint, band,
|
||||
target_atom_types=shifted_DOS) >= threshold_for_localization_to_be_shifted:
|
||||
kb_array.append([kpoint, band])
|
||||
self.frozen_mask.append(0)
|
||||
return kb_array
|
||||
|
||||
def calculate_kHET_spatial(self, tip_type='s', z_pos=None, from_center=False, cutoff_in_Angstroms=5, all_z=False, dim='2D', shifted_separately=False):
|
||||
xlen, ylen, zlen = np.shape(self.wavecar.wavefunctions[0])
|
||||
|
||||
if dim == '2D':
|
||||
k_HET_ = np.zeros((xlen, ylen))
|
||||
if z_pos == None:
|
||||
raise ValueError('For dim=2D z_pos is obligatory parameter')
|
||||
b1, b2, b3 = self.poscar.structure.lattice
|
||||
bn1 = b1 / xlen
|
||||
bn2 = b2 / ylen
|
||||
bn3 = b3 / zlen
|
||||
if b3[0] != 0.0 or b3[1] != 0.0:
|
||||
print("WARNING! You z_vector is not perpendicular to XY plane, Check calculate_kHET_spatial_2D function")
|
||||
if from_center:
|
||||
z = int(zlen // 2 + z_pos // np.linalg.norm(bn3))
|
||||
else:
|
||||
z = int(z_pos // np.linalg.norm(bn3))
|
||||
real_z_position = z_pos // np.linalg.norm(bn3) * np.linalg.norm(bn3)
|
||||
print(f"Real z_pos of tip = {real_z_position}")
|
||||
|
||||
if any(tip_type == kw for kw in self.AVAILABLE_TIPS_TYPES):
|
||||
cutoff = int(cutoff_in_Angstroms // np.linalg.norm(bn1))
|
||||
if cutoff > xlen // 2 or cutoff > ylen // 2:
|
||||
print("ERROR: Cutoff should be less than 1/2 of each dimension of the cell. "
|
||||
"Try to reduce cutoff. Otherwise, result could be unpredictible")
|
||||
acc_orbitals = self.generate_acceptor_orbitals(tip_type, (xlen, ylen, zlen), z_shift=z)
|
||||
if all_z == True:
|
||||
zmin = 0
|
||||
zmax = zlen
|
||||
elif z - cutoff >= 0 and z + cutoff <= zlen:
|
||||
zmin = z - cutoff
|
||||
zmax = z + cutoff
|
||||
else:
|
||||
print("Can't reduce integrating area in z dimention. "
|
||||
"Will calculate overlapping for all z. You can ignore this message "
|
||||
"if you don't care about efficiency")
|
||||
zmin = 0
|
||||
zmax = zlen
|
||||
|
||||
for i, kb in enumerate(self.wavecar.kb_array):
|
||||
kpoint, band = kb[0], kb[1]
|
||||
energy = self.outcar.eigenvalues[0][kpoint - 1][band - 1] - self.outcar.efermi
|
||||
weight = self.outcar.weights[0][kpoint - 1]
|
||||
f_fermi = GM.fermi_func(energy - self.dE_Q, self.T)
|
||||
w_redox = GM.W_ox(energy - self.dE_Q - self.overpot, self.T, self.lambda_)
|
||||
overlap_integrals_squared = self._get_overlap_integrals_squared(self.wavecar.wavefunctions[i], cutoff,
|
||||
acc_orbitals, zmin, zmax)
|
||||
# TODO check eq below
|
||||
matrix_elements_squared = overlap_integrals_squared * self.linear_constant * np.linalg.norm(bn3) ** 3
|
||||
k_HET_ += matrix_elements_squared * f_fermi * w_redox * weight
|
||||
|
||||
elif tip_type == 's':
|
||||
for i, kb in enumerate(self.wavecar.kb_array):
|
||||
kpoint, band = kb[0], kb[1]
|
||||
energy = self.outcar.eigenvalues[0][kpoint - 1][band - 1] - self.outcar.efermi
|
||||
weight = self.outcar.weights[0][kpoint - 1]
|
||||
f_fermi = GM.fermi_func(energy - self.dE_Q, self.T)
|
||||
w_redox = GM.W_ox(energy - self.dE_Q - self.overpot, self.T, self.lambda_)
|
||||
matrix_elements_squared = np.abs(self.wavecar.wavefunctions[i][:, :, z]) ** 2
|
||||
k_HET_ += matrix_elements_squared * f_fermi * w_redox * weight
|
||||
|
||||
elif tip_type == 'pz':
|
||||
for i, kb in enumerate(self.wavecar.kb_array):
|
||||
kpoint, band = kb[0], kb[1]
|
||||
energy = self.outcar.eigenvalues[0][kpoint - 1][band - 1] - self.outcar.efermi
|
||||
weight = self.outcar.weights[0][kpoint - 1]
|
||||
f_fermi = GM.fermi_func(energy - self.dE_Q, self.T)
|
||||
w_redox = GM.W_ox(energy - self.dE_Q - self.overpot, self.T, self.lambda_)
|
||||
wf_grad_z = np.gradient(self.wavecar.wavefunctions[i], axis=2)
|
||||
matrix_elements_squared = np.abs(wf_grad_z[:, :, z]) ** 2
|
||||
k_HET_ += matrix_elements_squared * f_fermi * w_redox * weight
|
||||
else:
|
||||
print(f"Try another tip type, for now {tip_type} is unavailiable")
|
||||
|
||||
elif dim == '3D':
|
||||
k_HET_ = np.zeros((xlen, ylen, zlen))
|
||||
|
||||
if tip_type == 's':
|
||||
if self.are_frozen_states:
|
||||
if shifted_separately:
|
||||
k_HET_shifted = np.zeros((xlen, ylen, zlen))
|
||||
for i, kb in enumerate(self.wavecar.kb_array):
|
||||
kpoint, band = kb[0], kb[1]
|
||||
energy = self.outcar.eigenvalues[0][kpoint - 1][band - 1] - self.outcar.efermi
|
||||
weight = self.outcar.weights[kpoint - 1]
|
||||
if self.frozen_mask[i] == 1:
|
||||
f_fermi = GM.fermi_func(energy, self.T)
|
||||
w_redox = GM.W_ox(energy - self.overpot, self.T, self.lambda_)
|
||||
matrix_elements_squared = np.abs(self.wavecar.wavefunctions[i]) ** 2
|
||||
k_HET_ += matrix_elements_squared * f_fermi * w_redox * weight
|
||||
else:
|
||||
f_fermi = GM.fermi_func(energy - self.dE_Q, self.T)
|
||||
w_redox = GM.W_ox(energy - self.dE_Q - self.overpot, self.T, self.lambda_)
|
||||
matrix_elements_squared = np.abs(self.wavecar.wavefunctions[i]) ** 2
|
||||
k_HET_ += matrix_elements_squared * f_fermi * w_redox * weight
|
||||
try:
|
||||
k_HET_shifted += matrix_elements_squared * f_fermi * w_redox * weight
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
for i, kb in enumerate(self.wavecar.kb_array):
|
||||
kpoint, band = kb[0], kb[1]
|
||||
energy = self.outcar.eigenvalues[0][kpoint - 1][band - 1] - self.outcar.efermi
|
||||
weight = self.outcar.weights[0][kpoint - 1]
|
||||
f_fermi = GM.fermi_func(energy - self.dE_Q, self.T)
|
||||
w_redox = GM.W_ox(energy - self.dE_Q - self.overpot, self.T, self.lambda_)
|
||||
matrix_elements_squared = np.abs(self.wavecar.wavefunctions[i]) ** 2
|
||||
k_HET_ += matrix_elements_squared * f_fermi * w_redox * weight
|
||||
|
||||
elif tip_type == 'pz':
|
||||
if self.are_frozen_states:
|
||||
if shifted_separately:
|
||||
k_HET_shifted = np.zeros((xlen, ylen, zlen))
|
||||
for i, kb in enumerate(self.wavecar.kb_array):
|
||||
kpoint, band = kb[0], kb[1]
|
||||
energy = self.outcar.eigenvalues[0][kpoint - 1][band - 1] - self.outcar.efermi
|
||||
weight = self.outcar.weights[0][kpoint - 1]
|
||||
if self.frozen_mask[i] == 1:
|
||||
f_fermi = GM.fermi_func(energy, self.T)
|
||||
w_redox = GM.W_ox(energy - self.overpot, self.T, self.lambda_)
|
||||
wf_grad_z = np.gradient(self.wavecar.wavefunctions[i], axis=2)
|
||||
matrix_elements_squared = np.abs(wf_grad_z) ** 2
|
||||
k_HET_ += matrix_elements_squared * f_fermi * w_redox * weight
|
||||
else:
|
||||
f_fermi = GM.fermi_func(energy - self.dE_Q, self.T)
|
||||
w_redox = GM.W_ox(energy - self.dE_Q - self.overpot, self.T, self.lambda_)
|
||||
wf_grad_z = np.gradient(self.wavecar.wavefunctions[i], axis=2)
|
||||
matrix_elements_squared = np.abs(wf_grad_z) ** 2
|
||||
k_HET_ += matrix_elements_squared * f_fermi * w_redox * weight
|
||||
try:
|
||||
k_HET_shifted += matrix_elements_squared * f_fermi * w_redox * weight
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
for i, kb in enumerate(self.wavecar.kb_array):
|
||||
kpoint, band = kb[0], kb[1]
|
||||
energy = self.outcar.eigenvalues[0][kpoint - 1][band - 1] - self.outcar.efermi
|
||||
weight = self.outcar.weights[0][kpoint - 1]
|
||||
f_fermi = GM.fermi_func(energy - self.dE_Q, self.T)
|
||||
w_redox = GM.W_ox(energy - self.dE_Q - self.overpot, self.T, self.lambda_)
|
||||
wf_grad_z = np.gradient(self.wavecar.wavefunctions[i], axis=2)
|
||||
matrix_elements_squared = np.abs(wf_grad_z) ** 2
|
||||
k_HET_ += matrix_elements_squared * f_fermi * w_redox * weight
|
||||
|
||||
else:
|
||||
raise ValueError("dim should be 3D or 2D")
|
||||
|
||||
# TODO: check THIS below
|
||||
#k_HET_ *= 2 * np.pi / constants.PLANCK_CONSTANT
|
||||
if shifted_separately:
|
||||
return k_HET_, k_HET_shifted
|
||||
else:
|
||||
return k_HET_
|
||||
|
||||
def plot_2D(self, func, show=True, save=False, filename='fig.png', method='linear', logscale=None):
|
||||
"""
|
||||
Function for plotting 2D images
|
||||
"""
|
||||
xlen, ylen = np.shape(func)
|
||||
b1, b2, b3 = self.poscar.structure.lattice
|
||||
bn1 = b1 / xlen
|
||||
bn2 = b2 / ylen
|
||||
|
||||
R = []
|
||||
for j in range(ylen):
|
||||
for i in range(xlen):
|
||||
R.append(i * bn1 + j * bn2)
|
||||
X = np.array([x[0] for x in R])
|
||||
Y = np.array([x[1] for x in R])
|
||||
|
||||
Z = func.transpose().flatten()
|
||||
xi = np.linspace(X.min(), X.max(), 1000)
|
||||
yi = np.linspace(Y.min(), Y.max(), 1000)
|
||||
zi = griddata((X, Y), Z, (xi[None, :], yi[:, None]), method=method)
|
||||
if logscale == None:
|
||||
plt.contourf(xi, yi, zi, 500, cmap=plt.cm.rainbow)
|
||||
else:
|
||||
zi = np.log10(zi)
|
||||
min, max, step = logscale
|
||||
levels = np.arange(min, max, step)
|
||||
plt.contourf(xi, yi, zi, 500, cmap=plt.cm.rainbow, levels=levels)
|
||||
ax = plt.gca()
|
||||
ax.set_aspect('equal')
|
||||
divider = make_axes_locatable(ax)
|
||||
cax = divider.append_axes("right", size="7%", pad=0.1)
|
||||
cbar = plt.colorbar(cax=cax)
|
||||
if show == True:
|
||||
plt.show()
|
||||
if save == True:
|
||||
plt.savefig(filename, dpi=300)
|
||||
plt.close()
|
||||
|
||||
def _calculate_dE_Q(self):
|
||||
"""
|
||||
This function calls GerischerMarcus module with GM class to calculate distribution of redox species
|
||||
and Fermi-Dirac distribution according to Gerischer-Marcus formalism
|
||||
:return:
|
||||
"""
|
||||
gm = GM(path_to_data=self.path_to_data)
|
||||
gm.set_params(self.C_EDL, self.T, self.lambda_, self.sheet_area)
|
||||
return gm.compute_distributions(self.V_std, overpot=self.overpot, add_info=True)[2]
|
||||
|
||||
def _get_sheet_area(self):
|
||||
"""
|
||||
Inner function to calculate sheet_area (XY plane) in cm^2
|
||||
"""
|
||||
b1, b2, b3 = self.poscar.structure.lattice
|
||||
return np.linalg.norm(np.cross(b1, b2))*1e-16
|
||||
|
||||
def generate_acceptor_orbitals(self, orb_type, shape, z_shift=0, x_shift=0, y_shift=0):
|
||||
"""
|
||||
This is generator of acceptor orbitals using tip_types.py module
|
||||
:return:
|
||||
"""
|
||||
bohr_radius = 0.529177 #TODO Check
|
||||
b1, b2, b3 = self.poscar.structure.lattice
|
||||
bn1 = b1 / shape[0]
|
||||
bn2 = b2 / shape[1]
|
||||
bn3 = b3 / shape[2]
|
||||
basis = np.array([bn1, bn2, bn3])
|
||||
transition_matrix = basis.transpose()
|
||||
number_of_orbitals = len(tip_types.orbitals(orb_type))
|
||||
acc_orbitals = []
|
||||
for i in range(number_of_orbitals):
|
||||
acc_orbitals.append(np.zeros(shape))
|
||||
for i in range(shape[0]):
|
||||
for j in range(shape[1]):
|
||||
for k in range(shape[2]):
|
||||
if i - x_shift >= shape[0] / 2:
|
||||
x = i - shape[0] - x_shift
|
||||
else:
|
||||
x = i - x_shift
|
||||
if j - y_shift >= shape[1] / 2:
|
||||
y = j - shape[1] - y_shift
|
||||
else:
|
||||
y = j - y_shift
|
||||
if k - z_shift >= shape[2] / 2:
|
||||
z = k - shape[2] - z_shift
|
||||
else:
|
||||
z = k - z_shift
|
||||
r = np.dot(transition_matrix, np.array([x, y, z])) / bohr_radius
|
||||
for o, orbital in enumerate(acc_orbitals):
|
||||
orbital[i][j][k] = tip_types.orbitals(orb_type)[o](r)
|
||||
return acc_orbitals
|
||||
|
||||
def _get_overlap_integrals_squared(self, wf, cutoff, acc_orbitals, zmin, zmax):
|
||||
xlen, ylen, zlen = np.shape(wf)
|
||||
overlap_integrals_squared = np.zeros((xlen, ylen))
|
||||
for i in range(xlen):
|
||||
if i - cutoff < 0:
|
||||
wf_rolled_x = np.roll(wf, cutoff, axis=0)
|
||||
orb_rolled_x = []
|
||||
for orbital in acc_orbitals:
|
||||
orb_rolled_x.append(np.roll(orbital, i + cutoff, axis=0))
|
||||
xmin = i
|
||||
xmax = i + cutoff * 2
|
||||
elif i - cutoff >= 0 and i + cutoff <= xlen:
|
||||
wf_rolled_x = np.copy(wf)
|
||||
orb_rolled_x = []
|
||||
for orbital in acc_orbitals:
|
||||
orb_rolled_x.append(np.roll(orbital, i, axis = 0))
|
||||
xmin = i - cutoff
|
||||
xmax = i + cutoff
|
||||
elif i + cutoff > xlen:
|
||||
wf_rolled_x = np.roll(wf, -cutoff, axis=0)
|
||||
orb_rolled_x = []
|
||||
for orbital in acc_orbitals:
|
||||
orb_rolled_x.append(np.roll(orbital, i - cutoff, axis=0))
|
||||
xmin = i - cutoff * 2
|
||||
xmax = i
|
||||
else:
|
||||
print(f"ERROR: for i = {i} something with rolling arrays along x goes wrong")
|
||||
for j in range(ylen):
|
||||
if j - cutoff < 0:
|
||||
wf_rolled = np.roll(wf_rolled_x, cutoff, axis=1)
|
||||
orb_rolled = []
|
||||
for orbital in orb_rolled_x:
|
||||
orb_rolled.append(np.roll(orbital, j + cutoff, axis=1))
|
||||
ymin = j
|
||||
ymax = j + cutoff * 2
|
||||
elif j - cutoff >= 0 and j + cutoff <= ylen:
|
||||
wf_rolled = np.copy(wf_rolled_x)
|
||||
orb_rolled = []
|
||||
for orbital in orb_rolled_x:
|
||||
orb_rolled.append(np.roll(orbital, j, axis=1))
|
||||
ymin = j - cutoff
|
||||
ymax = j + cutoff
|
||||
elif j + cutoff > ylen:
|
||||
wf_rolled = np.roll(wf_rolled_x, -cutoff, axis=1)
|
||||
orb_rolled = []
|
||||
for orbital in orb_rolled_x:
|
||||
orb_rolled.append(np.roll(orbital, j - cutoff, axis=1))
|
||||
ymin = j - cutoff * 2
|
||||
ymax = j
|
||||
else:
|
||||
print(f"ERROR: for i = {i} something with rolling arrays along y goes wrong")
|
||||
|
||||
integral = []
|
||||
for orbital in orb_rolled:
|
||||
integral.append(np.linalg.norm(wf_rolled[xmin:xmax, ymin:ymax, zmin:zmax]*\
|
||||
orbital[xmin:xmax, ymin:ymax, zmin:zmax]))
|
||||
overlap_integrals_squared[i][j] = max(integral) ** 2
|
||||
return overlap_integrals_squared
|
||||
|
||||
def save_as_cube(self, array, name, dir):
|
||||
# TODO: rewrite
|
||||
import os
|
||||
if not os.path.exists(dir):
|
||||
print(f"Directory {dir} does not exist. Creating directory")
|
||||
os.mkdir(dir)
|
||||
|
||||
shape = np.shape(array)
|
||||
with open(self.working_folder + '/POSCAR') as inf: #TODO get data from poscar class would be better
|
||||
lines = inf.readlines()
|
||||
natoms = sum(map(int, lines[6].strip().split()))
|
||||
atomtypes = lines[5].strip().split()
|
||||
numbers_of_atoms = list(map(int, lines[6].strip().split()))
|
||||
type_of_i_atom = []
|
||||
basis = []
|
||||
for i in [2, 3, 4]:
|
||||
vector = list(map(float, lines[i].strip().split()))
|
||||
basis.append(vector)
|
||||
basis = np.array(basis)
|
||||
for i, number in enumerate(numbers_of_atoms):
|
||||
for j in range(number):
|
||||
type_of_i_atom.append(atomtypes[i])
|
||||
|
||||
with open(dir + '/' + name + '.cube', 'w') as ouf:
|
||||
ouf.write(' This file is generated using stm.py module\n')
|
||||
ouf.write(' Good luck\n')
|
||||
ouf.write(' ' + str(natoms) + '\t0.000\t0.000\t0.000\n')
|
||||
ouf.write(' ' + str(-shape[0]) + lines[2])
|
||||
ouf.write(' ' + str(-shape[1]) + lines[3])
|
||||
ouf.write(' ' + str(-shape[2]) + lines[4])
|
||||
for i, line in enumerate(lines[8:natoms + 8]):
|
||||
coordinate = np.array(list(map(float, line.strip().split())))
|
||||
if lines[7].strip() == 'Direct':
|
||||
coordinate = coordinate.dot(basis)
|
||||
coordinate *= constants.BOHR_RADIUS
|
||||
elif lines[7].strip() == 'Cartesian':
|
||||
coordinate *= constants.BOHR_RADIUS
|
||||
else:
|
||||
print('WARNING!!! Cannot read POSCAR correctly')
|
||||
atomtype = type_of_i_atom[i]
|
||||
atomnumber = list(constants.ElemNum2Name.keys())[list(constants.ElemNum2Name.values()).index(atomtype)]
|
||||
ouf.write(
|
||||
' ' + str(atomnumber) + '\t0.00000\t' + str(coordinate[0]) + '\t' + str(coordinate[1]) + '\t' + str(
|
||||
coordinate[2]) + '\n')
|
||||
counter = 0
|
||||
for i in range(shape[0]):
|
||||
for j in range(shape[1]):
|
||||
for k in range(shape[2]):
|
||||
ouf.write(str('%.5E' % array[i][j][k]) + ' ')
|
||||
counter += 1
|
||||
if counter % 6 == 0:
|
||||
ouf.write('\n ')
|
||||
ouf.write('\n ')
|
||||
print(f"File {name} saved")
|
||||
12
electrochemistry/echem/eltransfer/ldos.py
Normal file
12
electrochemistry/echem/eltransfer/ldos.py
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
|
||||
class Ldos():
|
||||
"""
|
||||
The aim of this class is to produce LDOS images (2d or 3d) in different energy ranges
|
||||
"""
|
||||
# TODO:
|
||||
# 1) One should get all necessary data from WAVECAR (process WAVECAR with energy step dE and
|
||||
# produce electron denstites
|
||||
#
|
||||
#
|
||||
571
electrochemistry/echem/eltransfer/preprocessing.py
Normal file
571
electrochemistry/echem/eltransfer/preprocessing.py
Normal file
@@ -0,0 +1,571 @@
|
||||
from monty.re import regrep
|
||||
from pymatgen.io.vasp.outputs import Locpot
|
||||
from pymatgen.io.vasp.outputs import Procar
|
||||
from electrochemistry.core.vaspwfc_p3 import vaspwfc
|
||||
import numpy as np
|
||||
import multiprocessing as mp
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.ndimage.filters import gaussian_filter1d
|
||||
|
||||
|
||||
class Preprocessing():
|
||||
"""
|
||||
This class is for basic preprocessing VASP output files (WAVECAR, OUTCAR, LOCPOT)
|
||||
Main goal for this class is to extract necessary date from VASP files and save data to .npy files:
|
||||
efermi - Fermi level
|
||||
eigenvalues - eigenvalues for each kpoint and band
|
||||
ndands - number of bands
|
||||
nkpts - number of kpoints
|
||||
occupations - occupations for each kpoint and band
|
||||
weights - weights for each kpoint
|
||||
WF_ijke - "effective" wavefunction 3D grids for each small energy range from E to E+dE
|
||||
"""
|
||||
|
||||
def __init__(self, working_folder='', print_out=False, dir_to_save=None):
|
||||
"""
|
||||
Initialization of variables
|
||||
"""
|
||||
self.print_out = print_out
|
||||
self.working_folder = working_folder
|
||||
if dir_to_save == None:
|
||||
self.dir_to_save = working_folder+'/Saved_data'
|
||||
else:
|
||||
self.dir_to_save = dir_to_save
|
||||
self.efermi = None
|
||||
self.nkpts = None
|
||||
self.nbands = None
|
||||
self.weights = None
|
||||
self.eigenvalues = None
|
||||
self.occupations = None
|
||||
self.vacuum_lvl = None
|
||||
self.procar_data = None
|
||||
self.ion_names = None
|
||||
self.orbital_names = None
|
||||
self.wavecar_path = working_folder+'/WAVECAR'
|
||||
self.outcar_path = working_folder+'/OUTCAR'
|
||||
self.poscar_path = working_folder+'/POSCAR'
|
||||
self.procar_path = working_folder+'/PROCAR'
|
||||
self.locpot_path = working_folder+'/LOCPOT'
|
||||
|
||||
def get_wavefunction(self, mesh_shape, energies, kb_array, weights, arr_real, arr_imag, arr_cd, done_number, n_grid, wavecar_path=None):
|
||||
"""
|
||||
This function is inner function for processes.
|
||||
It extracts wavefunction from WAVECAR using vaspwfc library (see. https://github.com/QijingZheng/VaspBandUnfolding)
|
||||
:param mesh_shape: shape of wavefucntion mesh (tuple of 3 int)
|
||||
:param energies: list of energies for current process to be done (list of int)
|
||||
:param kb_array: array of kpoints and bands which lay in current energy range (np.array of tuples (k,b) depends on energies)
|
||||
:param weights: np.array of float64 containes weights of different k-points (np.array of float depends on energies)
|
||||
:param arr_real: shared memory float64 array to put real part of wavefunction
|
||||
:param arr_imag: shared memory float64 array to put imaginary part of wavefunction
|
||||
:param done_number: shared memory int to controll number of done energy ranges
|
||||
:param wavecar_path: path to WAVECAR file
|
||||
:param n_grid: float parameter (multipicator) to increase mesh density (1.0 - standart grid, higher values e.g. 1.5, 2.0 - to inrease quality of grid)
|
||||
:return:
|
||||
"""
|
||||
if wavecar_path == None:
|
||||
wavecar_path = self.wavecar_path
|
||||
t_p = time.time()
|
||||
wavecar = vaspwfc(wavecar_path)
|
||||
#print("Reading WAVECAR takes", time.time()-t_p, " sec")
|
||||
#sys.stdout.flush()
|
||||
for e in energies:
|
||||
#t = time.time()
|
||||
n = mesh_shape[0]*mesh_shape[1]*mesh_shape[2]
|
||||
start = e * n
|
||||
kb_array_for_e = kb_array[e]
|
||||
WF_for_current_Energy_real = np.zeros(mesh_shape)
|
||||
WF_for_current_Energy_imag = np.zeros(mesh_shape)
|
||||
CD_for_current_Energy = np.zeros(mesh_shape)
|
||||
for kb in kb_array_for_e:
|
||||
kpoint = kb[0]
|
||||
band = kb[1]
|
||||
wf = wavecar.wfc_r(ikpt=kpoint, iband=band, ngrid=wavecar._ngrid * n_grid)
|
||||
phi_real = np.real(wf)
|
||||
phi_imag = np.imag(wf)
|
||||
phi_squared = np.abs(wf)**2
|
||||
#print ("Wavefunction done from process:", e, "; kpoint = ", kpoint, "; band = ", band)
|
||||
#sys.stdout.flush()
|
||||
WF_for_current_Energy_real += phi_real * np.sqrt(weights[kpoint - 1])
|
||||
WF_for_current_Energy_imag += phi_imag * np.sqrt(weights[kpoint - 1])
|
||||
CD_for_current_Energy += phi_squared * weights[kpoint - 1]
|
||||
WF_for_current_Energy_1D_real = np.reshape(WF_for_current_Energy_real, n)
|
||||
WF_for_current_Energy_1D_imag = np.reshape(WF_for_current_Energy_imag, n)
|
||||
CD_for_current_Energy_1D = np.reshape(CD_for_current_Energy, n)
|
||||
for ii in range(n):
|
||||
arr_real[ii + start] = float(WF_for_current_Energy_1D_real[ii])
|
||||
arr_imag[ii + start] = float(WF_for_current_Energy_1D_imag[ii])
|
||||
arr_cd[ii + start] = float(CD_for_current_Energy_1D[ii])
|
||||
#print ("Energy ", e, " finished, it takes: ", time.time()-t, " sec")
|
||||
#sys.stdout.flush()
|
||||
done_number.value += 1
|
||||
if self.print_out == True:
|
||||
print(done_number.value, " energies are done")
|
||||
sys.stdout.flush()
|
||||
|
||||
def process_WAVECAR(self, Erange_from_fermi, dE, wavecar_path=None, dir_to_save=None, n_grid=1.0):
|
||||
"""
|
||||
This function is based on vaspwfc class https://github.com/QijingZheng/VaspBandUnfolding
|
||||
This function process WAVECAR file obtained with VASP. It saves 4D np.array WF_ijke.npy
|
||||
which containes spacially resolved _complex128 effective wavefunctions for each energy range [E, E+dE]
|
||||
'Effective' means that we sum wavefunctions with close energies.
|
||||
UPDATE: The function saves also dictionary WF_data.npy, which contains two keys: 'energy_range','dE'
|
||||
:param Erange_from_fermi: Energy range for wich we extract wavefunction (tuple or list of float)
|
||||
:param dE: Energy step (float)
|
||||
:param wavecar_path: path to WAVECAR file (string)
|
||||
:param dir_to_save: directory to save WF_ijke.npy file (string)
|
||||
:param n_grid: float parameter (multipicator) to increase mesh density (1.0 - standart grid, higher values e.g. 1.5, 2.0 - to inrease quality of grid)
|
||||
:return: nothing
|
||||
"""
|
||||
if wavecar_path == None:
|
||||
wavecar_path = self.wavecar_path
|
||||
if dir_to_save == None:
|
||||
dir_to_save = self.dir_to_save
|
||||
for var in ['efermi', 'nkpts', 'nbands', 'eigenvalues', 'weights']:
|
||||
self.check_existance(var)
|
||||
Erange = [round(Erange_from_fermi[0] + self.efermi, 5), round(Erange_from_fermi[1] + self.efermi, 5)]
|
||||
energies_number = int((Erange[1] - Erange[0]) / dE)
|
||||
kb_array = [[] for i in range(energies_number)]
|
||||
wavecar = vaspwfc(wavecar_path)
|
||||
wf = wavecar.wfc_r(ikpt=1, iband=1, ngrid=wavecar._ngrid * n_grid)
|
||||
mesh_shape = np.shape(wf)
|
||||
n = mesh_shape[0]*mesh_shape[1]*mesh_shape[2]
|
||||
for band in range(1, self.nbands+1):
|
||||
for kpoint in range(1, self.nkpts+1):
|
||||
energy = self.eigenvalues[kpoint - 1][band - 1]
|
||||
if energy >= Erange[0] and energy < Erange[1]:
|
||||
e = int((energy - Erange[0]) / dE)
|
||||
kb_array[e].append([kpoint, band])
|
||||
t_arr = time.time()
|
||||
arr_real = mp.Array('f', [0.0] * n * energies_number)
|
||||
arr_imag = mp.Array('f', [0.0] * n * energies_number)
|
||||
arr_cd = mp.Array('f', [0.0] * n * energies_number)
|
||||
done_number = mp.Value('i', 0)
|
||||
if self.print_out == True:
|
||||
print("Array Created, it takes: ", time.time() - t_arr, " sec")
|
||||
processes = []
|
||||
if self.print_out == True:
|
||||
print("Total Number Of Energies = ", energies_number)
|
||||
numCPU = mp.cpu_count()
|
||||
Energies_per_CPU = energies_number // numCPU + 1
|
||||
for i in range(numCPU):
|
||||
if (i + 1) * Energies_per_CPU > energies_number:
|
||||
energies = np.arange(i * Energies_per_CPU, energies_number)
|
||||
else:
|
||||
energies = np.arange(i * Energies_per_CPU, (i + 1) * Energies_per_CPU)
|
||||
p = mp.Process(target=self.get_wavefunction, args=(mesh_shape, energies, kb_array, self.weights, arr_real, arr_imag, arr_cd, done_number, n_grid, wavecar_path))
|
||||
processes.append(p)
|
||||
if self.print_out == True:
|
||||
print(numCPU, " CPU Available")
|
||||
for step in range(len(processes) // numCPU + 1):
|
||||
for i in range(step * numCPU, (step + 1) * numCPU):
|
||||
try:
|
||||
processes[i].start()
|
||||
except:
|
||||
pass
|
||||
for i in range(step * numCPU, (step + 1) * numCPU):
|
||||
try:
|
||||
processes[i].join()
|
||||
except:
|
||||
pass
|
||||
if self.print_out == True:
|
||||
print("All energies DONE! Start wavefunction reshaping")
|
||||
arr = np.zeros(n * energies_number, dtype=np.complex_)
|
||||
arr.real = arr_real
|
||||
arr.imag = arr_imag
|
||||
wave_functions = np.reshape(arr, (energies_number,) + mesh_shape)
|
||||
wave_functions = np.moveaxis(wave_functions, 0, -1)
|
||||
|
||||
charge_densities = np.reshape(arr_cd, (energies_number,) + mesh_shape)
|
||||
charge_densities = np.moveaxis(charge_densities, 0, -1)
|
||||
|
||||
WF_data = {'energy_range':Erange_from_fermi, 'dE':dE}
|
||||
np.save(dir_to_save+'/WF_ijke', wave_functions)
|
||||
np.save(dir_to_save+'/CD_ijke', charge_densities)
|
||||
if self.print_out == True:
|
||||
print(dir_to_save+'/WF_ijke.npy Saved')
|
||||
print(dir_to_save+'/CD_ijke.npy Saved')
|
||||
np.save(dir_to_save+'/WF_data', WF_data)
|
||||
if self.print_out == True:
|
||||
print(dir_to_save+'/WF_data.npy Saved')
|
||||
|
||||
|
||||
def process_OUTCAR(self, outcar_path=None, dir_to_save=None, optimization=False):
|
||||
"""
|
||||
process OUTCAR file obtained from VASP
|
||||
get following variables:
|
||||
self.nkpts - number of k-points (int)
|
||||
self.efermi - Fermi level (float)
|
||||
self.nbands - number of bands (int)
|
||||
self.eigenvalues - 2D np.array, eigenvalues[i][j] contains energy for i k-point and j band
|
||||
self.occupations - 2D np.array, occupations[i][j] contains occupation for i k-point and j band
|
||||
:param outcar_path: path to OUTCAR file
|
||||
:return: nothing
|
||||
"""
|
||||
if outcar_path == None:
|
||||
outcar_path = self.outcar_path
|
||||
if dir_to_save == None:
|
||||
dir_to_save = self.dir_to_save
|
||||
patterns = {'nkpts': r'Found\s+(\d+)\s+irreducible\sk-points',
|
||||
'weights':'Following reciprocal coordinates:',
|
||||
'efermi': 'E-fermi\s:\s+([-.\d]+)',
|
||||
'kpoints': r'k-point\s+(\d+)\s:\s+[-.\d]+\s+[-.\d]+\s+[-.\d]+\n'}
|
||||
matches = regrep(outcar_path, patterns)
|
||||
|
||||
self.nkpts = int(matches['nkpts'][0][0][0])
|
||||
if optimization:
|
||||
self.efermi = []
|
||||
efermi_data = np.array(matches['efermi'])[...,0]
|
||||
number_of_ionic_steps = len(efermi_data)
|
||||
for i in range(number_of_ionic_steps):
|
||||
self.efermi.append(float(efermi_data[i][0]))
|
||||
else:
|
||||
self.efermi = float(matches['efermi'][0][0][0])
|
||||
self.nbands = int(matches['kpoints'][1][1] - matches['kpoints'][0][1]-3)
|
||||
self.eigenvalues = []
|
||||
self.occupations = []
|
||||
self.weights = []
|
||||
|
||||
with open(outcar_path) as file:
|
||||
lines = file.readlines()
|
||||
for i in range(self.nkpts):
|
||||
self.weights.append(float(lines[matches['weights'][0][1]+i+2].split()[3]))
|
||||
if optimization:
|
||||
for step in range(number_of_ionic_steps):
|
||||
self.eigenvalues.append([])
|
||||
self.occupations.append([])
|
||||
for kpoint in range(self.nkpts):
|
||||
self.eigenvalues[step].append([])
|
||||
self.occupations[step].append([])
|
||||
startline = matches['kpoints'][kpoint+(step*self.nkpts)][1] + 2
|
||||
for i in range(startline, startline + self.nbands):
|
||||
self.eigenvalues[step][kpoint].append(float(lines[i].split()[1]))
|
||||
self.occupations[step][kpoint].append(float(lines[i].split()[2]))
|
||||
else:
|
||||
for kpoint in range(self.nkpts):
|
||||
self.eigenvalues.append([])
|
||||
self.occupations.append([])
|
||||
startline = matches['kpoints'][kpoint][1]+2
|
||||
for i in range(startline, startline + self.nbands):
|
||||
self.eigenvalues[kpoint].append(float(lines[i].split()[1]))
|
||||
self.occupations[kpoint].append(float(lines[i].split()[2]))
|
||||
self.eigenvalues = np.array(self.eigenvalues)
|
||||
self.occupations = np.array(self.occupations)
|
||||
self.weights = np.array(self.weights)
|
||||
self.weights /= np.sum(self.weights)
|
||||
for var in ['efermi', 'nkpts', 'nbands', 'weights', 'eigenvalues', 'occupations']:
|
||||
self.save(var, dir_to_save)
|
||||
|
||||
def process_PROCAR(self, procar_path=None, poscar_path=None, dir_to_save=None):
|
||||
"""
|
||||
This function process PROCAR file obtained by VASP using pymatgen.io_data.vasp.outputs.Procar class
|
||||
and saves ion_names, orbital_names and data array
|
||||
data array contains projections in the following form: data[kpoint][band][ion_number][orbital_number]
|
||||
All numberings start from 0
|
||||
:param procar_path: path to PROCAR file
|
||||
:param poscar_path: path to POSCAR file
|
||||
:param dir_to_save: directory to save data
|
||||
:return:
|
||||
"""
|
||||
if procar_path == None:
|
||||
procar_path = self.procar_path
|
||||
if poscar_path == None:
|
||||
poscar_path = self.poscar_path
|
||||
if dir_to_save == None:
|
||||
dir_to_save = self.dir_to_save
|
||||
procar=Procar(procar_path)
|
||||
for key in procar.data.keys():
|
||||
self.procar_data = procar.data[key]
|
||||
self.orbital_names = procar.orbitals
|
||||
self.ion_names = self._get_atom_types(poscar_path=poscar_path)
|
||||
for var in ['procar_data', 'orbital_names', 'ion_names']:
|
||||
self.save(var, dir_to_save)
|
||||
|
||||
def _get_atom_types(self, poscar_path=None):
|
||||
"""
|
||||
Inner function to obtain list of atom types from POSCAR
|
||||
:param file_path: path to POSCAR file
|
||||
:return: atom_types 1D array with atom_types. Index from 0
|
||||
"""
|
||||
if poscar_path == None:
|
||||
poscar_path = self.poscar_path
|
||||
with open(poscar_path) as inf:
|
||||
lines = inf.readlines()
|
||||
ion_types = lines[5].strip().split()
|
||||
nions = map(int, lines[6].strip().split())
|
||||
atom_types=[]
|
||||
for i, number in enumerate(nions):
|
||||
for j in range(number):
|
||||
atom_types.append(ion_types[i])
|
||||
return atom_types
|
||||
|
||||
def process_LOCPOT(self, locpot_path=None, dir_to_save=None):
|
||||
"""
|
||||
This function process LOCPOT file obtained by VASP
|
||||
:param file_path: path to LOCPOT file
|
||||
:param dir_to_save: path to directory to save vacuum_lvl
|
||||
:return: nothing
|
||||
"""
|
||||
if locpot_path == None:
|
||||
locpot_path = self.locpot_path
|
||||
if dir_to_save == None:
|
||||
dir_to_save = self.dir_to_save
|
||||
locpot = Locpot.from_file(locpot_path)
|
||||
avr = locpot.get_average_along_axis(2)
|
||||
self.vacuum_lvl = np.max(avr)
|
||||
self.save('vacuum_lvl', dir_to_save)
|
||||
|
||||
def save(self, variable='all', dir=None):
|
||||
"""
|
||||
This function saves variables in .npy files
|
||||
:param variable: desired variable
|
||||
:param dir: directory name to save file
|
||||
:return: nothing
|
||||
"""
|
||||
if dir == None:
|
||||
dir = self.dir_to_save
|
||||
if variable == 'all':
|
||||
for var in ['efermi', 'nkpts', 'nbands', 'weights', 'eigenvalues', 'occupations', 'vacuum_lvl']:
|
||||
self.save(var)
|
||||
else:
|
||||
if not os.path.exists(dir):
|
||||
if self.print_out == True:
|
||||
print('Directory ', dir, ' does not exist. Creating directory')
|
||||
os.mkdir(dir)
|
||||
np.save(dir+'/'+variable+'.npy', getattr(self, variable))
|
||||
if self.print_out == True:
|
||||
print('Variable ', variable, ' saved to directory ', dir)
|
||||
|
||||
def load(self, variable='all', dir=None):
|
||||
"""
|
||||
This function loads variables from .npy files to class variables
|
||||
:param variable: desired variable
|
||||
:param dir: directory from which load files
|
||||
:return: nothing
|
||||
"""
|
||||
if dir == None:
|
||||
dir = self.dir_to_save
|
||||
if variable == 'all':
|
||||
for var in ['efermi', 'nkpts', 'nbands', 'weights', 'eigenvalues', 'occupations', 'vacuum_lvl']:
|
||||
self.load(var)
|
||||
else:
|
||||
setattr(self, variable, np.load(dir+'/'+str(variable)+'.npy'))
|
||||
if self.print_out == True:
|
||||
print('Variable ', variable, ' loaded')
|
||||
|
||||
def check_existance(self, variable='all', dir=None):
|
||||
"""
|
||||
This function checks whether desires variable is not None and if necessary load it from file or process VASP data
|
||||
:param variable: desired variable
|
||||
:param dir: directory in which check .npy saved data
|
||||
:return: nothing
|
||||
"""
|
||||
if dir == None:
|
||||
dir = self.dir_to_save
|
||||
if variable == 'all':
|
||||
for var in ['efermi', 'nkpts', 'nbands', 'weights', 'eigenvalues', 'occupations', 'vacuum_lvl']:
|
||||
self.check_existance(var)
|
||||
else:
|
||||
if getattr(self, variable) is None:
|
||||
try:
|
||||
if self.print_out == True:
|
||||
print('Try load variable ',variable, ' from dir ', dir)
|
||||
self.load(variable, dir)
|
||||
except:
|
||||
if variable == 'vacuum_lvl':
|
||||
if self.print_out == True:
|
||||
print('Loading ', variable, ' failed! Start processing LOCPOT')
|
||||
self.process_LOCPOT()
|
||||
elif variable == 'procar_data' or variable == 'orbital_names' \
|
||||
or variable == 'ion_names':
|
||||
if self.print_out == True:
|
||||
print('Loading ', variable, ' failed! Start processing PROCAR and POSCAR')
|
||||
self.process_PROCAR()
|
||||
else:
|
||||
if self.print_out == True:
|
||||
print('Loading ', variable, ' failed! Start processing OUTCAR')
|
||||
self.process_OUTCAR()
|
||||
else:
|
||||
if self.print_out == True:
|
||||
print('Variable ', variable, 'exists')
|
||||
|
||||
def get_band_eigs(self, band, outcar_path='OUTCAR'):
|
||||
"""
|
||||
This function get eigenvalues for desired band
|
||||
:param band: desired band
|
||||
:param outcar_path: path to OUTCAR file
|
||||
:return:
|
||||
"""
|
||||
if self.eigenvalues is None:
|
||||
try:
|
||||
if self.print_out == True:
|
||||
print("Variable is not define. Try load from file")
|
||||
self.load('eigenvalues')
|
||||
except:
|
||||
if self.print_out == True:
|
||||
print("Loading failed. Start processing OUTCAR")
|
||||
self.process_OUTCAR(outcar_path)
|
||||
return self.eigenvalues[:,band]
|
||||
else:
|
||||
return self.eigenvalues[:,band]
|
||||
|
||||
def get_band_occ(self, band, outcar_path='OUTCAR'):
|
||||
"""
|
||||
This function get occupations for desired band
|
||||
:param band: desired band
|
||||
:param outcar_path: path to OUTCAR file
|
||||
:return:
|
||||
"""
|
||||
if self.occupations is None:
|
||||
try:
|
||||
if self.print_out == True:
|
||||
print("Variable is not define. Try load from file")
|
||||
self.load('occupations')
|
||||
except:
|
||||
if self.print_out == True:
|
||||
print("Loading failed. Start processing OUTCAR")
|
||||
self.process_OUTCAR(outcar_path)
|
||||
return self.occupations[:, band]
|
||||
else:
|
||||
return self.occupations[:, band]
|
||||
|
||||
def get_DOS(self, Erange_from_fermi, dE, optimization=False):
|
||||
"""
|
||||
This function calculates Density of States for desired energy range and energy step
|
||||
:param Erange_from_fermi: Energy range
|
||||
:param dE: energy step
|
||||
:param dE_new : recalculated energy step to enshure integer number of steps
|
||||
:return:
|
||||
E_arr: float64 np.array with energies relative to Fermi level
|
||||
DOS_arr: float64 np.array with Density of States
|
||||
"""
|
||||
|
||||
for var in ['efermi', 'nkpts', 'nbands', 'eigenvalues', 'weights']:
|
||||
self.check_existance(var)
|
||||
if optimization:
|
||||
DOS_arr = []
|
||||
E_arr = []
|
||||
for step in range(len(self.efermi)):
|
||||
Erange = [Erange_from_fermi[0] + self.efermi[step], Erange_from_fermi[1] + self.efermi[step]]
|
||||
energies_number = int((Erange[1] - Erange[0]) / dE)
|
||||
dE_new = (Erange[1] - Erange[0]) / energies_number
|
||||
DOS_arr.append(np.zeros(energies_number))
|
||||
E_arr.append(np.arange(Erange_from_fermi[0], Erange_from_fermi[1], dE_new))
|
||||
for k in range(self.nkpts):
|
||||
for b in range(self.nbands):
|
||||
energy = self.eigenvalues[step][k][b]
|
||||
if energy > Erange[0] and energy < Erange[1]:
|
||||
e = int((energy - Erange[0]) / dE_new)
|
||||
DOS_arr[step][e] += self.weights[k] / dE_new
|
||||
return E_arr, DOS_arr * 2
|
||||
else:
|
||||
Erange = [Erange_from_fermi[0] + self.efermi, Erange_from_fermi[1] + self.efermi]
|
||||
energies_number = int((Erange[1] - Erange[0]) / dE)
|
||||
dE_new = (Erange[1] - Erange[0])/energies_number
|
||||
DOS_arr = np.zeros(energies_number)
|
||||
E_arr = np.arange(Erange_from_fermi[0], Erange_from_fermi[1], dE_new)
|
||||
for k in range(self.nkpts):
|
||||
for b in range(self.nbands):
|
||||
energy = self.eigenvalues[k][b]
|
||||
if energy > Erange[0] and energy < Erange[1]:
|
||||
e = int((energy - Erange[0]) / dE_new)
|
||||
DOS_arr[e]+=self.weights[k] / dE_new
|
||||
return E_arr, DOS_arr * 2, dE_new
|
||||
|
||||
def get_pdos(self, Erange_from_fermi, dE, ions='all', orbitals='all', dir_to_data=None):
|
||||
if dir_to_data == None:
|
||||
dir_to_data = self.dir_to_save
|
||||
for var in ['procar_data' , 'orbital_names', 'ion_names', 'efermi', 'eigenvalues', 'weights']:
|
||||
self.check_existance(var, dir_to_data)
|
||||
|
||||
Erange = [Erange_from_fermi[0] + self.efermi, Erange_from_fermi[1] + self.efermi]
|
||||
energies_number = int((Erange[1] - Erange[0]) / dE)
|
||||
dE_new = (Erange[1] - Erange[0]) / energies_number
|
||||
DOS_arr = np.zeros(energies_number)
|
||||
E_arr = np.arange(Erange_from_fermi[0], Erange_from_fermi[1], dE_new)
|
||||
nkpts = np.shape(self.procar_data)[0]
|
||||
nbands = np.shape(self.procar_data)[1]
|
||||
for k in range(nkpts):
|
||||
if self.print_out == True:
|
||||
print('kpoint = ', k)
|
||||
for b in range(nbands):
|
||||
energy = self.eigenvalues[k][b]
|
||||
if energy > Erange[0] and energy < Erange[1]:
|
||||
e = int((energy - Erange[0]) / dE_new)
|
||||
if ions == 'all':
|
||||
list_of_ions = [i for i in range(len(self.ion_names))]
|
||||
elif type(ions) == str:
|
||||
list_of_ions = []
|
||||
for i, name in enumerate(self.ion_names):
|
||||
if name == ions:
|
||||
list_of_ions.append(i)
|
||||
elif type(ions) == list:
|
||||
if type(ions[0]) == int:
|
||||
list_of_ions = ions
|
||||
elif type(ions[0]) == str:
|
||||
list_of_ions = []
|
||||
for ion_name in ions:
|
||||
for i, name in enumerate(self.ion_names):
|
||||
if name == ion_name:
|
||||
list_of_ions.append(i)
|
||||
if orbitals == 'all':
|
||||
list_of_orbitals = [i for i in range(len(self.orbital_names))]
|
||||
elif type(orbitals) == str:
|
||||
list_of_orbitals = []
|
||||
for i, name in enumerate(self.orbital_names):
|
||||
if name == orbitals:
|
||||
list_of_orbitals.append(i)
|
||||
elif type(orbitals) == list:
|
||||
if type(orbitals[0]) == int:
|
||||
list_of_orbitals = ions
|
||||
elif type(orbitals[0]) == str:
|
||||
list_of_orbitals = []
|
||||
for orb_name in orbitals:
|
||||
for i, name in enumerate(self.orbital_names):
|
||||
if name == orb_name:
|
||||
list_of_orbitals.append(i)
|
||||
weight = 0
|
||||
for ion in list_of_ions:
|
||||
for orb in list_of_orbitals:
|
||||
weight+=self.procar_data[k][b][ion][orb]
|
||||
DOS_arr[e] += weight / dE_new * self.weights[k]
|
||||
return E_arr, DOS_arr * 2, dE_new
|
||||
|
||||
if __name__=='__main__':
|
||||
t=time.time()
|
||||
"""
|
||||
outcar_path = 'OUTCAR'
|
||||
wavecar_path = 'WAVECAR'
|
||||
dir_to_save = "Saved_data"
|
||||
p = Preprocessing(dir_to_save)
|
||||
p.process_OUTCAR(outcar_path=outcar_path, dir_to_save=dir_to_save)
|
||||
print('Processing OUTCAR takes: ', time.time()-t, 'sec')
|
||||
t=time.time()
|
||||
p.process_WAVECAR((-7.0, 4.0), 0.01, wavecar_path=wavecar_path, dir_to_save=dir_to_save)
|
||||
print('Job done! Processing WAVECAR: ', time.time()-t, ' sec')
|
||||
"""
|
||||
dir_to_save = 'Saved_data'
|
||||
p = Preprocessing(dir_to_save)
|
||||
Erange = [-25.0, 5.0]
|
||||
dE = 0.1
|
||||
E_arr, DOS_arr, dE_new = p.get_DOS(Erange, dE)
|
||||
plt.plot(E_arr, gaussian_filter1d(DOS_arr, sigma=2), label='total')
|
||||
E_arr, PDOS_arr, dE_new = p.get_pdos(Erange, dE, ions='C', orbitals='s')
|
||||
plt.plot(E_arr, gaussian_filter1d(PDOS_arr, sigma=2), label='C-s')
|
||||
E_arr, PDOS_arr, dE_new = p.get_pdos(Erange, dE, ions='C', orbitals=['px', 'py', 'pz'])
|
||||
plt.plot(E_arr, gaussian_filter1d(PDOS_arr, sigma=2), label='C-p')
|
||||
E_arr, PDOS_arr, dE_new = p.get_pdos(Erange, dE, ions='H', orbitals='s')
|
||||
plt.plot(E_arr, gaussian_filter1d(PDOS_arr, sigma=2), label='H-s')
|
||||
E_arr, PDOS_arr, dE_new = p.get_pdos(Erange, dE, ions='O', orbitals='s')
|
||||
plt.plot(E_arr, gaussian_filter1d(PDOS_arr, sigma=2), label='O-s')
|
||||
E_arr, PDOS_arr, dE_new = p.get_pdos(Erange, dE, ions='O', orbitals=['px', 'py', 'pz'])
|
||||
plt.plot(E_arr, gaussian_filter1d(PDOS_arr, sigma=2), label='O-p')
|
||||
plt.xlabel('E, eV')
|
||||
plt.ylabel('DOS, states/eV/cell')
|
||||
plt.legend()
|
||||
plt.savefig('pdos.png', dpi=300)
|
||||
plt.show()
|
||||
plt.close()
|
||||
|
||||
811
electrochemistry/echem/eltransfer/stm.py
Normal file
811
electrochemistry/echem/eltransfer/stm.py
Normal file
@@ -0,0 +1,811 @@
|
||||
import numpy as np
|
||||
import os
|
||||
import time
|
||||
import math
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.interpolate import griddata
|
||||
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
||||
from scipy.ndimage.filters import gaussian_filter1d
|
||||
import tip_types
|
||||
from echem.core import ElemNum2Name
|
||||
plt.switch_backend('agg')
|
||||
|
||||
|
||||
class STM:
|
||||
"""
|
||||
This class is for computing 2D and 3D STM images with different theory levels:
|
||||
Tersoff-Hummann, Chen, analytical acceptor wavefucntions (oxygen)
|
||||
Also this class can calculate 2D ECSTM images using GerisherMarcus module
|
||||
|
||||
In current version of program this class is no more needed. Most of functions are relocated to kHET_spatial.py
|
||||
"""
|
||||
|
||||
PLANCK_CONSTANT = 4.135667662e-15 # Planck's constant in eV*s
|
||||
BOLTZMANN_CONSTANT = 8.617333262145e-5 # Boltzmann's constant in eV/K
|
||||
ELEM_CHARGE = 1.60217662e-19 # Elementary charge in Coulombs
|
||||
BOHR_RADIUS = 1.88973
|
||||
AVAILABLE_TIPS_TYPES = ['oxygen', 'IrCl6', 'RuNH3_6', 'RuNH3_6_NNN_plane', 'RuNH3_6_perpendicular', 'oxygen_parallel_x', 'oxygen_parallel_y']
|
||||
|
||||
def __init__(self, working_folder):
|
||||
self.WF_ijke = None
|
||||
self.CD_ijke = None
|
||||
self.dE = None
|
||||
self.energy_range = None
|
||||
self.working_folder = working_folder
|
||||
self.path_to_data = self.working_folder+'/Saved_data'
|
||||
self.WF_data_path = self.path_to_data+'/WF_data.npy'
|
||||
self.WF_ijke_path = self.path_to_data+'/WF_ijke.npy'
|
||||
self.CD_ijke_path = self.path_to_data+'/CD_ijke.npy'
|
||||
self.poscar_path = working_folder+'/POSCAR'
|
||||
|
||||
def save_as_vasp(self, array, name, dir):
|
||||
if not os.path.exists(dir):
|
||||
print(f"Directory {dir} does not exist. Creating directory")
|
||||
os.mkdir(dir)
|
||||
shape = np.shape(array)
|
||||
with open(self.poscar_path) as inf:
|
||||
lines = inf.readlines()
|
||||
natoms = int(lines[6].strip())
|
||||
with open(dir+'/'+ name + '.vasp', 'w') as ouf:
|
||||
ouf.writelines(lines[:natoms+8])
|
||||
ouf.write('\n ' + str(shape[0]) + ' ' + str(shape[1]) + ' ' + str(shape[2]) + '\n ')
|
||||
counter = 0
|
||||
for k in range(shape[2]):
|
||||
for j in range(shape[1]):
|
||||
for i in range(shape[0]):
|
||||
ouf.write(str('%.8E' % array[i][j][k]) + ' ')
|
||||
counter += 1
|
||||
if counter % 10 == 0:
|
||||
ouf.write('\n ')
|
||||
print(f"File {name} saved")
|
||||
|
||||
def save_as_cube(self, array, name, dir):
|
||||
if not os.path.exists(dir):
|
||||
print(f"Directory {dir} does not exist. Creating directory")
|
||||
os.mkdir(dir)
|
||||
|
||||
shape = np.shape(array)
|
||||
with open(self.poscar_path) as inf:
|
||||
lines = inf.readlines()
|
||||
natoms = sum(map(int, lines[6].strip().split()))
|
||||
atomtypes = lines[5].strip().split()
|
||||
numbers_of_atoms = list(map(int, lines[6].strip().split()))
|
||||
type_of_i_atom = []
|
||||
basis = []
|
||||
for i in [2,3,4]:
|
||||
vector = list(map(float, lines[i].strip().split()))
|
||||
basis.append(vector)
|
||||
basis = np.array(basis)
|
||||
for i, number in enumerate(numbers_of_atoms):
|
||||
for j in range(number):
|
||||
type_of_i_atom.append(atomtypes[i])
|
||||
|
||||
with open(dir+'/'+ name + '.cube', 'w') as ouf:
|
||||
ouf.write(' This file is generated using stm.py module\n')
|
||||
ouf.write(' Good luck\n')
|
||||
ouf.write(' ' + str(natoms) + '\t0.000\t0.000\t0.000\n')
|
||||
ouf.write(' ' + str(-shape[0]) + lines[2])
|
||||
ouf.write(' ' + str(-shape[1]) + lines[3])
|
||||
ouf.write(' ' + str(-shape[2]) + lines[4])
|
||||
for i, line in enumerate(lines[8:natoms+8]):
|
||||
coordinate = np.array(list(map(float, line.strip().split())))
|
||||
if lines[7].strip() == 'Direct':
|
||||
coordinate = coordinate.dot(basis)
|
||||
coordinate *= self.BOHR_RADIUS
|
||||
elif lines[7].strip() == 'Cartesian':
|
||||
coordinate *= self.BOHR_RADIUS
|
||||
else:
|
||||
print ('WARNING!!! Cannot read POSCAR correctly')
|
||||
atomtype = type_of_i_atom[i]
|
||||
atomnumber = list(ElemNum2Name.keys())[list(ElemNum2Name.values()).index(atomtype)]
|
||||
ouf.write(' ' + str(atomnumber) + '\t0.00000\t' + str(coordinate[0])+ '\t' + str(coordinate[1])+ '\t' + str(coordinate[2])+'\n')
|
||||
counter = 0
|
||||
for i in range(shape[0]):
|
||||
for j in range(shape[1]):
|
||||
for k in range(shape[2]):
|
||||
ouf.write(str('%.5E' % array[i][j][k]) + ' ')
|
||||
counter += 1
|
||||
if counter % 6 == 0:
|
||||
ouf.write('\n ')
|
||||
ouf.write('\n ')
|
||||
print(f"File {name} saved")
|
||||
|
||||
|
||||
def load_data(self):
|
||||
WF_data = np.load(self.WF_data_path, allow_pickle=True).item()
|
||||
self.dE = WF_data['dE']
|
||||
self.energy_range = WF_data['energy_range']
|
||||
self.WF_ijke = np.load(self.WF_ijke_path)
|
||||
self.CD_ijke = np.load(self.CD_ijke_path)
|
||||
|
||||
def set_ecstm_parameters(self, C_EDL, T, lambda_, V_std, overpot,
|
||||
effective_freq, linear_constant, threshold_value):
|
||||
"""
|
||||
:param C_EDL: float
|
||||
Capacitance of electric double layer (microF/cm^2)
|
||||
:param T: int, float
|
||||
Temperature. It is used in computing Fermi function and distribution function of redox system states
|
||||
:param lambda_: float
|
||||
Reorganization energy in eV
|
||||
:param V_std: float
|
||||
Standart potential of the redox couple (Volts)
|
||||
:param overpot: float
|
||||
Overpotential (Volts). It shifts the electrode Fermi energy to -|e|*overpot
|
||||
:param effective_freq: float
|
||||
Effective frequency of redox species motion
|
||||
:param effective_freq: float
|
||||
Linear constant of proportionality of Hif and Sif: Hif = linear_constant * Sif
|
||||
:param threshold_value: float
|
||||
Minimum value of y_redox and y_fermi to be considered in integral
|
||||
:return:
|
||||
"""
|
||||
self.C_EDL = C_EDL
|
||||
self.T = T
|
||||
self.lambda_ = lambda_
|
||||
self.sheet_area = self._get_sheet_area() # Area of investigated surface(XY) in cm^2
|
||||
self.V_std = V_std
|
||||
self.overpot = overpot
|
||||
self.effective_freq = effective_freq
|
||||
self.linear_constant = linear_constant
|
||||
self.threshold_value = threshold_value
|
||||
|
||||
def load_data_for_ecstm(self):
|
||||
"""
|
||||
This inner function load necessary data for ECSTM calculations
|
||||
:param outcar_path: str
|
||||
path to OUTCAR vasp file
|
||||
:param locpot_path: str
|
||||
path to LOCPOT vasp file
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
self.E = np.load(self.path_to_data+'/E.npy')
|
||||
self.DOS = np.load(self.path_to_data+'/DOS.npy')
|
||||
except:
|
||||
import preprocessing_v2 as preprocessing
|
||||
p = preprocessing.Preprocessing(working_folder=self.working_folder)
|
||||
p.process_OUTCAR()
|
||||
self.E, self.DOS, dE_new = p.get_DOS(self.energy_range, self.dE)
|
||||
if dE_new != self.dE:
|
||||
print("WARNING! Something wrong with dE during DOS calculations")
|
||||
np.save(self.path_to_data+'/DOS.npy', self.DOS)
|
||||
np.save(self.path_to_data+'/E.npy', self.E)
|
||||
try:
|
||||
self.efermi = np.load(self.path_to_data+'/efermi.npy')
|
||||
except:
|
||||
print(f"ERROR! {self.path_to_data}/efermi.npy does not exist. Try to preprocess data")
|
||||
try:
|
||||
self.vacuum_lvl = np.load(self.path_to_data+'vacuum_lvl.npy')
|
||||
except:
|
||||
from pymatgen.io.vasp.outputs import Locpot
|
||||
locpot = Locpot.from_file(self.working_folder+'/LOCPOT')
|
||||
avr = locpot.get_average_along_axis(2)
|
||||
self.vacuum_lvl = np.max(avr)
|
||||
np.save(self.path_to_data+'/vacuum_lvl.npy', self.vacuum_lvl)
|
||||
|
||||
def _calculate_distributions(self):
|
||||
"""
|
||||
This function calls GerisherMarcus module with GM class to calculate distribution of redox species
|
||||
and Fermi-Dirac distribution according to Gerisher-Marcformalismizm
|
||||
:return:
|
||||
"""
|
||||
import GerischerMarkus as gm
|
||||
gerisher_marcus_obj = gm.GM(path_to_data = self.path_to_data)
|
||||
gerisher_marcus_obj.set_params(self.C_EDL, self.T, self.lambda_, self.sheet_area)
|
||||
self.y_fermi, self.y_redox = gerisher_marcus_obj.compute_distributions(self.V_std, overpot=self.overpot)
|
||||
return gerisher_marcus_obj
|
||||
|
||||
@staticmethod
|
||||
def _nearest_array_indices(array, value):
|
||||
i = 0
|
||||
while value > array[i]:
|
||||
i += 1
|
||||
return i - 1, i
|
||||
|
||||
def plot_distributions(self, E_range=[-7, 4], dE=None, sigma=2, fill_area_lower_Fermi_lvl=True,
|
||||
plot_Fermi_Dirac_distib=True, plot_redox_distrib=True):
|
||||
a = self._calculate_distributions()
|
||||
if dE == None:
|
||||
dE = self.dE
|
||||
if dE != self.dE or E_range[0] < self.energy_range[0] or E_range[1] > self.energy_range[1]:
|
||||
import preprocessing_v2 as prep
|
||||
p = prep.Preprocessing(working_folder = self.working_folder)
|
||||
E, DOS, dE_new = p.get_DOS(E_range, dE)
|
||||
else:
|
||||
E, DOS = a.E, a.DOS
|
||||
n_1, n_2 = self._nearest_array_indices(E, a.dE_Q_eq)
|
||||
if sigma > 0 and sigma != None:
|
||||
DOS = gaussian_filter1d(DOS, sigma)
|
||||
plt.plot(E, DOS)
|
||||
if fill_area_lower_Fermi_lvl == True:
|
||||
plt.fill_between(E[:n_2], DOS[:n_2])
|
||||
if plot_Fermi_Dirac_distib == True:
|
||||
plt.plot(a.E, self.y_fermi * 30)
|
||||
if plot_redox_distrib == True:
|
||||
plt.plot(a.E, self.y_redox * 10)
|
||||
plt.xlabel('E, eV')
|
||||
plt.ylabel('DOS, states/eV/cell')
|
||||
plt.xlim(E_range)
|
||||
plt.savefig(f"distributions_{self.V_std}_{self.overpot}.png", dpi=300)
|
||||
plt.close()
|
||||
|
||||
def _kT_in_eV(self, T):
|
||||
return T * self.BOLTZMANN_CONSTANT
|
||||
|
||||
def _get_kappa(self, matrix_elements_squared):
|
||||
lz_factor = 2 * matrix_elements_squared / self.PLANCK_CONSTANT / self.effective_freq * math.sqrt(
|
||||
math.pi / self.lambda_ / self._kT_in_eV(self.T))
|
||||
kappa = 1 - np.exp(-2 * math.pi * lz_factor)
|
||||
return kappa
|
||||
|
||||
def _get_basis_vectors(self):
|
||||
"""
|
||||
This function processes POSCAR file to extract vectors of the simulation box
|
||||
:param poscar_path:
|
||||
:return:
|
||||
"""
|
||||
with open(self.poscar_path) as inf:
|
||||
lines = inf.readlines()
|
||||
b1 = np.array(list(map(float, lines[2].strip().split())))
|
||||
b2 = np.array(list(map(float, lines[3].strip().split())))
|
||||
b3 = np.array(list(map(float, lines[4].strip().split())))
|
||||
return b1, b2, b3
|
||||
|
||||
def _get_sheet_area(self):
|
||||
"""
|
||||
Inner function to calculate sheet_area (XY plane) in cm^2
|
||||
"""
|
||||
b1, b2, b3 = self._get_basis_vectors()
|
||||
return np.linalg.norm(np.cross(b1,b2))*1e-16
|
||||
|
||||
def _plot_contour_map(self, function, X, Y, dir, filename):
|
||||
"""
|
||||
Function for plotting 2D images
|
||||
:param function: Z values
|
||||
:param X: X values
|
||||
:param Y: Y values
|
||||
:param dir: directory in which contour map will be saved
|
||||
:param filename: filename of image
|
||||
:return:
|
||||
"""
|
||||
if not os.path.exists(dir):
|
||||
print(f"Directory {dir} does not exist. Creating directory")
|
||||
os.mkdir(dir)
|
||||
|
||||
t = time.time()
|
||||
Z = function.transpose().flatten()
|
||||
xi = np.linspace(X.min(), X.max(), 1000)
|
||||
yi = np.linspace(Y.min(), Y.max(), 1000)
|
||||
zi = griddata((X, Y), Z, (xi[None, :], yi[:, None]), method='cubic')
|
||||
plt.contourf(xi, yi, zi, 500, cmap=plt.cm.rainbow)
|
||||
ax = plt.gca()
|
||||
ax.set_aspect('equal')
|
||||
divider = make_axes_locatable(ax)
|
||||
cax = divider.append_axes("right", size="7%", pad=0.1)
|
||||
plt.colorbar(cax=cax)
|
||||
plt.savefig(dir+'/'+filename+'.png', dpi=300)
|
||||
plt.close()
|
||||
print (f"Plotting map takes {time.time()-t} sec")
|
||||
|
||||
|
||||
def _get_overlap_integrals_squared(self, e, cutoff, acc_orbitals, zmin, zmax):
|
||||
WF_ijk = self.WF_ijke[:, :, :, e]
|
||||
xlen, ylen, zlen = np.shape(WF_ijk)
|
||||
overlap_integrals_squared = np.zeros((xlen, ylen))
|
||||
if np.allclose(WF_ijk, np.zeros((xlen, ylen, zlen))):
|
||||
# If WF_ijk array for current energy is empty, code goes to the next energy
|
||||
print(f"WF_ijk array for energy {e} is empty. Going to the next")
|
||||
return overlap_integrals_squared
|
||||
for i in range(xlen):
|
||||
if i - cutoff < 0:
|
||||
WF_ijk_rolled_x = np.roll(WF_ijk, cutoff, axis=0)
|
||||
orb_rolled_x = []
|
||||
for orbital in acc_orbitals:
|
||||
orb_rolled_x.append(np.roll(orbital, i + cutoff, axis=0))
|
||||
xmin = i
|
||||
xmax = i + cutoff * 2
|
||||
elif i - cutoff >= 0 and i + cutoff <= xlen:
|
||||
WF_ijk_rolled_x = np.copy(WF_ijk)
|
||||
orb_rolled_x = []
|
||||
for orbital in acc_orbitals:
|
||||
orb_rolled_x.append(np.roll(orbital, i, axis = 0))
|
||||
xmin = i - cutoff
|
||||
xmax = i + cutoff
|
||||
elif i + cutoff > xlen:
|
||||
WF_ijk_rolled_x = np.roll(WF_ijk, -cutoff, axis=0)
|
||||
orb_rolled_x = []
|
||||
for orbital in acc_orbitals:
|
||||
orb_rolled_x.append(np.roll(orbital, i - cutoff, axis=0))
|
||||
xmin = i - cutoff * 2
|
||||
xmax = i
|
||||
else:
|
||||
print(f"ERROR: for i = {i} something with rolling arrays along x goes wrong")
|
||||
for j in range(ylen):
|
||||
if j - cutoff < 0:
|
||||
WF_ijk_rolled = np.roll(WF_ijk_rolled_x, cutoff, axis=1)
|
||||
orb_rolled = []
|
||||
for orbital in orb_rolled_x:
|
||||
orb_rolled.append(np.roll(orbital, j + cutoff, axis=1))
|
||||
ymin = j
|
||||
ymax = j + cutoff * 2
|
||||
elif j - cutoff >= 0 and j + cutoff <= ylen:
|
||||
WF_ijk_rolled = np.copy(WF_ijk_rolled_x)
|
||||
orb_rolled = []
|
||||
for orbital in orb_rolled_x:
|
||||
orb_rolled.append(np.roll(orbital, j, axis=1))
|
||||
ymin = j - cutoff
|
||||
ymax = j + cutoff
|
||||
elif j + cutoff > ylen:
|
||||
WF_ijk_rolled = np.roll(WF_ijk_rolled_x, -cutoff, axis=1)
|
||||
orb_rolled = []
|
||||
for orbital in orb_rolled_x:
|
||||
orb_rolled.append(np.roll(orbital, j - cutoff, axis=1))
|
||||
ymin = j - cutoff * 2
|
||||
ymax = j
|
||||
else:
|
||||
print(f"ERROR: for i = {i} something with rolling arrays along y goes wrong")
|
||||
|
||||
integral = []
|
||||
for orbital in orb_rolled:
|
||||
integral.append(np.linalg.norm(WF_ijk_rolled[xmin:xmax, ymin:ymax, zmin:zmax]*\
|
||||
orbital[xmin:xmax, ymin:ymax, zmin:zmax]))
|
||||
overlap_integrals_squared[i][j] = max(integral) ** 2
|
||||
|
||||
return overlap_integrals_squared
|
||||
|
||||
def generate_acceptor_orbitals(self, orb_type, shape, z_shift=0, x_shift=0, y_shift=0):
|
||||
"""
|
||||
This is generator of acceptor orbitals using tip_types.py module
|
||||
:return:
|
||||
"""
|
||||
bohr_radius = 0.529 #TODO better to get it from core.constants
|
||||
b1, b2, b3 = self._get_basis_vectors()
|
||||
bn1 = b1 / shape[0]
|
||||
bn2 = b2 / shape[1]
|
||||
bn3 = b3 / shape[2]
|
||||
basis = np.array([bn1, bn2, bn3])
|
||||
transition_matrix = basis.transpose()
|
||||
numer_of_orbitals = len(tip_types.orbitals(orb_type))
|
||||
acc_orbitals = []
|
||||
for i in range(numer_of_orbitals):
|
||||
acc_orbitals.append(np.zeros(shape))
|
||||
for i in range(shape[0]):
|
||||
for j in range(shape[1]):
|
||||
for k in range(shape[2]):
|
||||
if i - x_shift >= shape[0] / 2:
|
||||
x = i - shape[0] - x_shift
|
||||
else:
|
||||
x = i - x_shift
|
||||
if j - y_shift >= shape[1] / 2:
|
||||
y = j - shape[1] - y_shift
|
||||
else:
|
||||
y = j - y_shift
|
||||
if k - z_shift >= shape[2] / 2:
|
||||
z = k - shape[2] - z_shift
|
||||
else:
|
||||
z = k - z_shift
|
||||
r = np.dot(transition_matrix, np.array([x, y, z])) / bohr_radius
|
||||
for o, orbital in enumerate(acc_orbitals):
|
||||
orbital[i][j][k] = tip_types.orbitals(orb_type)[o](r)
|
||||
return acc_orbitals
|
||||
|
||||
|
||||
def calculate_2D_ECSTM(self, z_position, tip_type='oxygen', dir='ECSTM', cutoff_in_Angstroms=5, all_z=False, from_CD=False):
|
||||
"""
|
||||
This function calculate 2D ECSTM images using GerisherMarcus module
|
||||
:param z_position: position of tip under the investigated surface
|
||||
:param tip_type: Type of tip orbital. Currenty only "oxygen" is available
|
||||
:param dir: directiry to save images
|
||||
:param cutoff_in_Angstroms: Cutoff over x,y and z, when overlap integral is calculated.
|
||||
E.g. for x direction area of integration will be from x-cutoff to x+cutoff
|
||||
:return:
|
||||
"""
|
||||
|
||||
print(f"Starting to calculate 2D ECSTM; tip_type = {tip_type};")
|
||||
xlen, ylen, zlen, elen = np.shape(self.WF_ijke)
|
||||
b1, b2, b3 = self._get_basis_vectors()
|
||||
bn1 = b1 / xlen
|
||||
bn2 = b2 / ylen
|
||||
bn3 = b3 / zlen
|
||||
|
||||
print (bn1, bn2, xlen, ylen)
|
||||
|
||||
if b3[0] != 0.0 or b3[1] != 0.0:
|
||||
print("WARNING! You z_vector is not perpendicular to XY plane, Check calculate_2D_STM function")
|
||||
|
||||
z = int(zlen // 2 + z_position // np.linalg.norm(bn3))
|
||||
real_z_position = z_position // np.linalg.norm(bn3) * np.linalg.norm(bn3)
|
||||
print(f"Real z_position of tip = {real_z_position}")
|
||||
|
||||
R = []
|
||||
for j in range(ylen):
|
||||
for i in range(xlen):
|
||||
R.append(i * bn1 + j * bn2)
|
||||
X = np.array([x[0] for x in R])
|
||||
Y = np.array([x[1] for x in R])
|
||||
|
||||
if any(tip_type == kw for kw in self.AVAILABLE_TIPS_TYPES):
|
||||
cutoff = int(cutoff_in_Angstroms // np.linalg.norm(bn1))
|
||||
if cutoff > xlen // 2 or cutoff > ylen // 2:
|
||||
print("ERROR: Cutoff should be less than 1/2 of each dimension of the cell. "
|
||||
"Try to reduce cutoff. Otherwise, result could be unpredictible")
|
||||
print(f"Cutoff in int = {cutoff}")
|
||||
ECSTM_ij = np.zeros((xlen, ylen))
|
||||
acc_orbitals = self.generate_acceptor_orbitals(tip_type, (xlen, ylen, zlen), z_shift=z)
|
||||
if all_z == True:
|
||||
zmin = 0
|
||||
zmax = zlen
|
||||
elif z - cutoff >= 0 and z + cutoff <= zlen:
|
||||
zmin = z - cutoff
|
||||
zmax = z + cutoff
|
||||
else:
|
||||
print("Can't reduce integrating area in z dimention. "
|
||||
"Will calculate overlapping for all z. You can ignore this message "
|
||||
"if you don't care about efficiency")
|
||||
zmin = 0
|
||||
zmax = zlen
|
||||
self._calculate_distributions()
|
||||
for e in range(elen):
|
||||
if self.y_redox[e] < self.threshold_value or self.y_fermi[e] < self.threshold_value:
|
||||
continue
|
||||
overlap_integrals_squared = self._get_overlap_integrals_squared(e, cutoff, acc_orbitals, zmin, zmax)
|
||||
matrix_elements_squared = overlap_integrals_squared * self.linear_constant * np.linalg.norm(bn3)**3
|
||||
#print ('Hif = ', matrix_elements_squared, 'eV')
|
||||
#kappa = self._get_kappa(matrix_elements_squared)
|
||||
#ECSTM_ij += kappa * self.y_fermi[e] * self.DOS[e] * self.y_redox[e] * self.dE * 2 * math.pi *\
|
||||
# self.ELEM_CHARGE / self.PLANCK_CONSTANT
|
||||
ECSTM_ij += 2 * np.pi / self.PLANCK_CONSTANT * matrix_elements_squared * self.y_fermi[e] * self.DOS[e] * self.y_redox[e] * self.dE
|
||||
|
||||
|
||||
|
||||
elif tip_type == 's':
|
||||
ECSTM_ij = np.zeros((xlen, ylen))
|
||||
self._calculate_distributions()
|
||||
for e in range(elen):
|
||||
if self.y_redox[e] < self.threshold_value or self.y_fermi[e] < self.threshold_value:
|
||||
continue
|
||||
if from_CD == True:
|
||||
matrix_elements_squared = self.CD_ijke[:, :, z, e]
|
||||
ECSTM_ij += 2 * np.pi / self.PLANCK_CONSTANT * matrix_elements_squared * self.y_fermi[e] * self.DOS[e] * self.y_redox[e] * self.dE
|
||||
else:
|
||||
matrix_elements_squared = np.abs(self.WF_ijke[:, :, z, e]) ** 2
|
||||
#print ('Hif = ', matrix_elements_squared, 'eV')
|
||||
#kappa = self._get_kappa(matrix_elements_squared)
|
||||
#ECSTM_ij += kappa * self.y_fermi[e] * self.DOS[e] * self.y_redox[e] * self.dE * 2 * math.pi *\
|
||||
# self.ELEM_CHARGE / self.PLANCK_CONSTANT\
|
||||
#TODO - check formula!!!
|
||||
ECSTM_ij += 2 * np.pi / self.PLANCK_CONSTANT * matrix_elements_squared * self.y_fermi[e] * self.DOS[e] * self.y_redox[e] * self.dE
|
||||
|
||||
|
||||
elif tip_type == 'pz':
|
||||
ECSTM_ij = np.zeros((xlen, ylen))
|
||||
self._calculate_distributions()
|
||||
for e in range(elen):
|
||||
if self.y_redox[e] < self.threshold_value or self.y_fermi[e] < self.threshold_value:
|
||||
continue
|
||||
grad_z_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=2)
|
||||
matrix_elements_squared = (np.abs(grad_z_WF_for_e[:, :, z])) ** 2
|
||||
print ('Hif = ', matrix_elements_squared, 'eV')
|
||||
#kappa = self._get_kappa(matrix_elements_squared)
|
||||
#ECSTM_ij += kappa * self.y_fermi[e] * self.DOS[e] * self.y_redox[e] * self.dE * 2 * math.pi *\
|
||||
# self.ELEM_CHARGE / self.PLANCK_CONSTANT
|
||||
ECSTM_ij += 2 * np.pi / self.PLANCK_CONSTANT * matrix_elements_squared * self.y_fermi[e] * self.DOS[e] * self.y_redox[e] * self.dE
|
||||
|
||||
if from_CD:
|
||||
cd_flag='from_CD'
|
||||
else:
|
||||
cd_flag=''
|
||||
filename = f"ecstm_{'%.2f'%real_z_position}_{tip_type}_{cd_flag}_{self.V_std}_{self.overpot}_{self.lambda_}"
|
||||
self._plot_contour_map(ECSTM_ij, X, Y, dir, filename)
|
||||
np.save(dir+'/'+filename, ECSTM_ij)
|
||||
|
||||
|
||||
def calculate_2D_STM(self, STM_energy_range, z_position, tip_type='s', dir='STM_2D', cutoff_in_Angstroms=5, all_z=False, from_CD=False):
|
||||
"""
|
||||
Function for calculation 2D STM images
|
||||
:param STM_energy_range: Energy range regarding Fermi level
|
||||
:param z_position: Position of tip under the surface. This distance is in Angstroms assuming
|
||||
that investigated surface XY is in the center of calculation cell (the middle of z axes)
|
||||
:param tip_type: type of tip orbital
|
||||
:param dir: directory for saving images
|
||||
:param cutoff_in_Angstroms: Cutoff over x,y and z, when overlap integral is calculated.
|
||||
E.g. for x direction area of integration will be from x-cutoff to x+cutoff
|
||||
:return:
|
||||
"""
|
||||
emin = int((STM_energy_range[0] - self.energy_range[0]) / self.dE)
|
||||
emax = int((STM_energy_range[1] - self.energy_range[0]) / self.dE)
|
||||
print(f"Starting to calculate 2D STM; tip_type = {tip_type}; STM energy range = {STM_energy_range}")
|
||||
e_array = np.arange(emin, emax)
|
||||
xlen, ylen, zlen, elen = np.shape(self.WF_ijke)
|
||||
b1, b2, b3 = self._get_basis_vectors()
|
||||
bn1 = b1 / xlen
|
||||
bn2 = b2 / ylen
|
||||
bn3 = b3 / zlen
|
||||
|
||||
if b3[0] != 0.0 or b3[1] != 0.0 :
|
||||
print("WARNING! You z_vector is not perpendicular to XY plane, Check calculate_2D_STM function")
|
||||
|
||||
z = int(zlen // 2 + z_position // np.linalg.norm(bn3))
|
||||
real_z_position = z_position // np.linalg.norm(bn3) * np.linalg.norm(bn3)
|
||||
print(f"Real z_position of tip = {real_z_position}")
|
||||
|
||||
R = []
|
||||
for j in range(ylen):
|
||||
for i in range(xlen):
|
||||
R.append(i * bn1 + j * bn2)
|
||||
X = np.array([x[0] for x in R])
|
||||
Y = np.array([x[1] for x in R])
|
||||
|
||||
if any(tip_type == kw for kw in self.AVAILABLE_TIPS_TYPES):
|
||||
cutoff = int(cutoff_in_Angstroms // np.linalg.norm(bn1))
|
||||
if cutoff > xlen//2 or cutoff > ylen//2:
|
||||
print("ERROR: Cutoff should be less than 1/2 of each dimension of the cell. "
|
||||
"Try to reduce cutoff. Otherwise, result could be unpredictible")
|
||||
print (f"Cutoff in int = {cutoff}")
|
||||
STM_ij = np.zeros((xlen, ylen))
|
||||
acc_orbitals = self.generate_acceptor_orbitals(tip_type, (xlen, ylen, zlen), z_shift=z)
|
||||
if all_z == True:
|
||||
zmin = 0
|
||||
zmax = zlen
|
||||
elif z - cutoff >=0 and z + cutoff <= zlen:
|
||||
zmin = z-cutoff
|
||||
zmax = z+cutoff
|
||||
else:
|
||||
print("Can't reduce integrating area in z dimention. "
|
||||
"Will calculate overlapping for all z. You can ignore this message "
|
||||
"if you don't care about efficiency")
|
||||
zmin = 0
|
||||
zmax = zlen
|
||||
for e in e_array:
|
||||
overlap_integrals_squared = self._get_overlap_integrals_squared(e, cutoff, acc_orbitals, zmin, zmax)
|
||||
STM_ij += overlap_integrals_squared
|
||||
|
||||
elif tip_type == 's':
|
||||
if from_CD == True:
|
||||
STM_ij = np.zeros((xlen, ylen))
|
||||
for e in e_array:
|
||||
STM_ij += self.CD_ijke[:, :, z, e]
|
||||
else:
|
||||
STM_ij = np.zeros((xlen, ylen))
|
||||
for e in e_array:
|
||||
STM_ij += np.abs(self.WF_ijke[:, :, z, e])**2
|
||||
|
||||
elif tip_type == 'pz':
|
||||
STM_ij = np.zeros((xlen, ylen))
|
||||
for e in e_array:
|
||||
grad_z_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=2)
|
||||
STM_ij += (np.abs(grad_z_WF_for_e[:, :, z])) ** 2
|
||||
|
||||
elif tip_type == 'px':
|
||||
STM_ij = np.zeros((xlen, ylen))
|
||||
for e in e_array:
|
||||
grad_x_WF_for_e = np.gradient(self.WF_ijke[:, :, z, e], axis=0)
|
||||
STM_ij += (np.abs(grad_x_WF_for_e)) ** 2
|
||||
|
||||
elif tip_type == 'p30':
|
||||
STM_ij = np.zeros((xlen, ylen))
|
||||
for e in e_array:
|
||||
grad_x_WF_for_e = np.gradient(self.WF_ijke[:, :, z, e], axis=0)
|
||||
grad_y_WF_for_e = np.gradient(self.WF_ijke[:, :, z, e], axis=1)
|
||||
STM_ij += (np.abs(grad_y_WF_for_e + grad_x_WF_for_e)) ** 2
|
||||
|
||||
elif tip_type == 'p60':
|
||||
STM_ij = np.zeros((xlen, ylen))
|
||||
for e in e_array:
|
||||
grad_y_WF_for_e = np.gradient(self.WF_ijke[:, :, z, e], axis=1)
|
||||
STM_ij += (np.abs(grad_y_WF_for_e)) ** 2
|
||||
|
||||
elif tip_type == 'p90':
|
||||
STM_ij = np.zeros((xlen, ylen))
|
||||
for e in e_array:
|
||||
grad_x_WF_for_e = np.gradient(self.WF_ijke[:, :, z, e], axis=0)
|
||||
grad_y_WF_for_e = np.gradient(self.WF_ijke[:, :, z, e], axis=1)
|
||||
STM_ij += (np.abs(grad_y_WF_for_e - grad_x_WF_for_e / 2.0)) ** 2
|
||||
|
||||
elif tip_type == 'p120':
|
||||
STM_ij = np.zeros((xlen, ylen))
|
||||
for e in e_array:
|
||||
grad_x_WF_for_e = np.gradient(self.WF_ijke[:, :, z, e], axis=0)
|
||||
grad_y_WF_for_e = np.gradient(self.WF_ijke[:, :, z, e], axis=1)
|
||||
STM_ij += (np.abs(grad_y_WF_for_e - grad_x_WF_for_e)) ** 2
|
||||
|
||||
elif tip_type == 'p150':
|
||||
STM_ij = np.zeros((xlen, ylen))
|
||||
for e in e_array:
|
||||
grad_x_WF_for_e = np.gradient(self.WF_ijke[:, :, z, e], axis=0)
|
||||
grad_y_WF_for_e = np.gradient(self.WF_ijke[:, :, z, e], axis=1)
|
||||
STM_ij += (np.abs(grad_y_WF_for_e / 2.0 - grad_x_WF_for_e)) ** 2
|
||||
|
||||
elif tip_type == 's+pz':
|
||||
STM_ij = np.zeros((xlen, ylen))
|
||||
for e in e_array:
|
||||
grad_z_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=2)
|
||||
STM_ij += (np.abs(self.WF_ijke[:, :, z, e] + grad_z_WF_for_e[:, :, z])) ** 2
|
||||
|
||||
if from_CD:
|
||||
cd_flag='from_CD'
|
||||
else:
|
||||
cd_flag=''
|
||||
filename = 'stm_' + str('%.2f' % real_z_position) + '_' + tip_type + '_' + cd_flag
|
||||
self._plot_contour_map(STM_ij, X, Y, dir, filename)
|
||||
np.save(dir+'/'+filename, STM_ij)
|
||||
|
||||
|
||||
def calculate_3D_STM(self, STM_energy_range, tip_type='s', dir='STM_3D', format='cube', from_CD=False):
|
||||
emin = int((STM_energy_range[0] - self.energy_range[0]) / self.dE)
|
||||
emax = int((STM_energy_range[1] - self.energy_range[0]) / self.dE)
|
||||
print ('Starting to calculate 3D STM; tip_type ='+tip_type+'; STM energy range = ', STM_energy_range)
|
||||
e_array = np.arange(emin, emax)
|
||||
xlen, ylen, zlen, elen = np.shape(self.WF_ijke)
|
||||
|
||||
if tip_type == 's':
|
||||
if from_CD == True:
|
||||
STM_ijk = np.zeros((xlen, ylen, zlen))
|
||||
for e in e_array:
|
||||
STM_ijk += self.CD_ijke[:, :, :, e]
|
||||
else:
|
||||
STM_ijk = np.zeros((xlen, ylen, zlen))
|
||||
for e in e_array:
|
||||
STM_ijk += np.abs(self.WF_ijke[:, :, :, e]) ** 2
|
||||
|
||||
elif tip_type == 'pz':
|
||||
STM_ijk = np.zeros((xlen, ylen, zlen))
|
||||
for e in e_array:
|
||||
grad_z_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=2)
|
||||
STM_ijk += (np.abs(grad_z_WF_for_e)) ** 2
|
||||
|
||||
elif tip_type == 'px':
|
||||
STM_ijk = np.zeros((xlen, ylen, zlen))
|
||||
for e in e_array:
|
||||
grad_x_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=0)
|
||||
STM_ijk += (np.abs(grad_x_WF_for_e)) ** 2
|
||||
|
||||
elif tip_type == 'p30':
|
||||
STM_ijk = np.zeros((xlen, ylen, zlen))
|
||||
for e in e_array:
|
||||
grad_x_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=0)
|
||||
grad_y_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=1)
|
||||
STM_ijk += (np.abs(grad_y_WF_for_e + grad_x_WF_for_e)) ** 2
|
||||
|
||||
elif tip_type == 'p60':
|
||||
STM_ijk = np.zeros((xlen, ylen, zlen))
|
||||
for e in e_array:
|
||||
grad_y_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=1)
|
||||
STM_ijk += (np.abs(grad_y_WF_for_e)) ** 2
|
||||
|
||||
elif tip_type == 'p90':
|
||||
STM_ijk = np.zeros((xlen, ylen, zlen))
|
||||
for e in e_array:
|
||||
grad_x_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=0)
|
||||
grad_y_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=1)
|
||||
STM_ijk += (np.abs(grad_y_WF_for_e - grad_x_WF_for_e / 2.0)) ** 2
|
||||
|
||||
elif tip_type == 'p120':
|
||||
STM_ijk = np.zeros((xlen, ylen, zlen))
|
||||
for e in e_array:
|
||||
grad_x_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=0)
|
||||
grad_y_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=1)
|
||||
STM_ijk += (np.abs(grad_y_WF_for_e - grad_x_WF_for_e)) ** 2
|
||||
|
||||
elif tip_type == 'p150':
|
||||
STM_ijk = np.zeros((xlen, ylen, zlen))
|
||||
for e in e_array:
|
||||
grad_x_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=0)
|
||||
grad_y_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=1)
|
||||
STM_ijk += (np.abs(grad_y_WF_for_e / 2.0 - grad_x_WF_for_e)) ** 2
|
||||
|
||||
elif tip_type == 's+pz':
|
||||
STM_ijk = np.zeros((xlen, ylen, zlen))
|
||||
for e in e_array:
|
||||
grad_z_WF_for_e = np.gradient(self.WF_ijke[:, :, :, e], axis=2)
|
||||
STM_ijk += (np.abs(self.WF_ijke[:, :, :, e] + grad_z_WF_for_e)) ** 2
|
||||
|
||||
if not os.path.exists(dir):
|
||||
print(f"Directory {dir} does not exist. Creating directory")
|
||||
os.mkdir(dir)
|
||||
|
||||
filename = 'stm_'+str(STM_energy_range[0])+'_'+str(STM_energy_range[1])+'_'+tip_type
|
||||
if format == 'vasp':
|
||||
self.save_as_vasp(STM_ijk, filename, dir)
|
||||
elif format == 'cube':
|
||||
self.save_as_cube(STM_ijk, filename, dir)
|
||||
|
||||
|
||||
if __name__=="__main__":
|
||||
t = time.time()
|
||||
stm = STM(path_to_data='Saved_data', poscar_path='../POSCAR')
|
||||
stm.load_data()
|
||||
|
||||
###CALCULATE 2D STM IMAGES###
|
||||
#stm.calculate_2D_STM((-0.5, 0.0), 3.5, tip_type='oxygen', cutoff_in_Angstroms=5)
|
||||
#stm.calculate_2D_STM((-0.5, 0.0), 3.0, tip_type='s')
|
||||
#stm.calculate_2D_STM((-0.5, 0.0), 3.0, tip_type='pz')
|
||||
#stm.calculate_2D_STM((-0.5, 0.0), 3.0, tip_type='s+pz')
|
||||
#stm.calculate_2D_STM((-0.5, 0.0), 4.5, tip_type='IrCl6', cutoff_in_Angstroms=8)
|
||||
#stm.calculate_2D_STM((-0.5, 0.0), 5.5, tip_type='RuNH3_6_NNN_plane', cutoff_in_Angstroms=8)
|
||||
|
||||
###CALCULATE 2D ECSTM IMAGES###
|
||||
stm.load_data_for_ecstm(outcar_path='../OUTCAR', locpot_path='../LOCPOT')
|
||||
|
||||
#for V_std in [-1.0, -0.5, 0.0, 0.5, 1.0]:
|
||||
#for V_std in [-0.5]:
|
||||
# stm.set_ecstm_parameters(C_EDL=20, T=298, lambda_=0.9, V_std=V_std,
|
||||
# overpot=0, effective_freq = 1E13, linear_constant=26, threshold_value=1e-5)
|
||||
# stm.plot_distributions(E_range=[-7, 4], dE=0.05, sigma=1, fill_area_lower_Fermi_lvl=True,
|
||||
# plot_Fermi_Dirac_distib=False, plot_redox_distrib=True)
|
||||
# #stm.calculate_2D_ECSTM(z_position=4.5, tip_type='IrCl6', cutoff_in_Angstroms=6)
|
||||
# #stm.calculate_2D_ECSTM(z_position=3.0, tip_type='s')
|
||||
# #stm.calculate_2D_ECSTM(z_position=3.5, tip_type='pz')
|
||||
# stm.calculate_2D_ECSTM(z_position=4.5, tip_type='s')
|
||||
|
||||
|
||||
#stm.set_ecstm_parameters(C_EDL=20, T=298, lambda_=1.63, V_std=-0.6,
|
||||
# overpot=0, effective_freq = 1E13, linear_constant=26, threshold_value=1e-5)
|
||||
#stm.plot_distributions(E_range=[-7, 4], dE=0.05, sigma=1, fill_area_lower_Fermi_lvl=True,
|
||||
# plot_Fermi_Dirac_distib=False, plot_redox_distrib=True)
|
||||
#stm.calculate_2D_ECSTM(z_position=3.5, tip_type='oxygen', cutoff_in_Angstroms=5)
|
||||
|
||||
|
||||
#stm.set_ecstm_parameters(C_EDL=20, T=298, lambda_=0.8, V_std=0.1,
|
||||
# overpot=0, effective_freq = 1E13, linear_constant=26, threshold_value=1e-5)
|
||||
#stm.plot_distributions(E_range=[-7, 4], dE=0.05, sigma=1, fill_area_lower_Fermi_lvl=True,
|
||||
# plot_Fermi_Dirac_distib=False, plot_redox_distrib=True)
|
||||
#stm.calculate_2D_ECSTM(z_position=5.5, tip_type='RuNH3_6_NNN_plane', cutoff_in_Angstroms=8)
|
||||
#stm.calculate_2D_ECSTM(z_position=3.0, tip_type='s')
|
||||
#stm.calculate_2D_ECSTM(z_position=3.0, tip_type='pz')
|
||||
#stm.calculate_2D_ECSTM(z_position=5.0, tip_type='s')
|
||||
#stm.calculate_2D_ECSTM(z_position=5.0, tip_type='pz')
|
||||
#stm.calculate_2D_ECSTM(z_position=4.5, tip_type='s')
|
||||
#stm.calculate_2D_ECSTM(z_position=4.5, tip_type='pz')
|
||||
|
||||
stm.set_ecstm_parameters(C_EDL=20, T=298, lambda_=1.2, V_std=0.87,
|
||||
overpot=0, effective_freq = 1E13, linear_constant=26, threshold_value=1e-5)
|
||||
stm.plot_distributions(E_range=[-7, 4], dE=0.05, sigma=1, fill_area_lower_Fermi_lvl=True,
|
||||
plot_Fermi_Dirac_distib=False, plot_redox_distrib=True)
|
||||
stm.calculate_2D_ECSTM(z_position=5.5, tip_type='IrCl6', cutoff_in_Angstroms=6)
|
||||
#stm.calculate_2D_ECSTM(z_position=3.0, tip_type='s')
|
||||
#stm.calculate_2D_ECSTM(z_position=3.0, tip_type='pz')
|
||||
#stm.calculate_2D_ECSTM(z_position=5.0, tip_type='s')
|
||||
#stm.calculate_2D_ECSTM(z_position=5.0, tip_type='pz')
|
||||
#stm.calculate_2D_ECSTM(z_position=4.5, tip_type='s')
|
||||
#stm.calculate_2D_ECSTM(z_position=4.5, tip_type='pz')
|
||||
|
||||
#stm.set_ecstm_parameters(C_EDL=20, T=298, lambda_=0.9, V_std=0.0,
|
||||
# overpot=0, effective_freq = 1E13, linear_constant=26, threshold_value=1e-5)
|
||||
#stm.plot_distributions(E_range=[-7, 4], dE=0.05, sigma=1, fill_area_lower_Fermi_lvl=True,
|
||||
# plot_Fermi_Dirac_distib=False, plot_redox_distrib=True)
|
||||
#stm.calculate_2D_ECSTM(z_position=3.0, tip_type='oxygen', cutoff_in_Angstroms=5)
|
||||
|
||||
|
||||
#stm.set_ecstm_parameters(C_EDL=20, T=298, lambda_= 1.0, V_std=-0.5,
|
||||
# overpot=0, effective_freq = 1E13, linear_constant=26, threshold_value=1e-5)
|
||||
#stm.plot_distributions(E_range=[-7, 4], dE=0.05, sigma=1, fill_area_lower_Fermi_lvl=True,
|
||||
# plot_Fermi_Dirac_distib=False, plot_redox_distrib=True)
|
||||
#stm.calculate_2D_ECSTM(z_position=3.0, tip_type='oxygen', cutoff_in_Angstroms=5)
|
||||
#stm.calculate_2D_ECSTM(z_position=5, tip_type='RuNH3_6_NNN_plane', cutoff_in_Angstroms=8, all_z=True)
|
||||
#stm.calculate_2D_ECSTM(z_position=7, tip_type='RuNH3_6_NNN_plane', cutoff_in_Angstroms=8, all_z=True)
|
||||
|
||||
|
||||
#stm.calculate_2D_ECSTM(z_position=4.5, tip_type='oxygen', cutoff_in_Angstroms=5)
|
||||
#stm.calculate_2D_ECSTM(z_position=3.5, tip_type='oxygen', cutoff_in_Angstroms=5)
|
||||
|
||||
##CACLULATE 3D STM IMAGE OPENABLE WITH VESTA PACKAGE###
|
||||
#stm.calculate_3D_STM((-0.5, 0.0), tip_type='s', format='cube')
|
||||
#stm.calculate_3D_STM((0.0, 0.5), tip_type='s', format='cube')
|
||||
|
||||
|
||||
###CHECK OXYGEN ORBITAL GENERATION###
|
||||
#acc_orbitals = stm.generate_acceptor_orbitals('RuNH3_6_NNN_plane', np.shape(stm.WF_ijke)[0:3], z_shift = 20, x_shift=50, y_shift=50)
|
||||
#for i, orbitals in enumerate(acc_orbitals):
|
||||
# stm.save_as_cube(orbitals, 'RuNH3_NNN'+str(i), 'orbitals')
|
||||
#acc_orbitals = stm.generate_acceptor_orbitals('RuNH3_6', np.shape(stm.WF_ijke)[0:3], z_shift = 20, x_shift=50, y_shift=50)
|
||||
#for i, orbitals in enumerate(acc_orbitals):
|
||||
# stm.save_as_cube(orbitals, 'RuNH3_standart_orient'+str(i), 'orbitals')
|
||||
#acc_orbitals = stm.generate_acceptor_orbitals('RuNH3_6_perpendicular', np.shape(stm.WF_ijke)[0:3], z_shift = 20, x_shift=50, y_shift=50)
|
||||
#for i, orbitals in enumerate(acc_orbitals):
|
||||
# stm.save_as_cube(orbitals, 'RuNH3_perpendicular'+str(i), 'orbitals')
|
||||
#acc_orbitals = stm.generate_acceptor_orbitals('oxygen_parallel_y', np.shape(stm.WF_ijke)[0:3], z_shift = 20, x_shift=50, y_shift=50)
|
||||
#for i, orbitals in enumerate(acc_orbitals):
|
||||
# stm.save_as_vasp(orbitals, 'ox_par_y_orb_'+str(i), 'orbitals')
|
||||
|
||||
#print(f"Job done! It takes: {time.time()-t} sec")
|
||||
398
electrochemistry/echem/eltransfer/tip_types.py
Normal file
398
electrochemistry/echem/eltransfer/tip_types.py
Normal file
@@ -0,0 +1,398 @@
|
||||
import numpy as np
|
||||
import math
|
||||
|
||||
|
||||
def read_orbital_data(filename):
|
||||
data=[]
|
||||
with open(filename) as inp:
|
||||
lines = inp.readlines()
|
||||
for line in lines:
|
||||
arr = line.strip().split()
|
||||
try:
|
||||
if arr[0] == 'GTF:':
|
||||
a = list(line[44:61])
|
||||
for n, i in enumerate(a):
|
||||
if i=='D':
|
||||
a[n]='E'
|
||||
a = float(''.join(a))
|
||||
if a!=0:
|
||||
b = list(line[65:79])
|
||||
for n, i in enumerate(b):
|
||||
if i == 'D':
|
||||
b[n] = 'E'
|
||||
b = float(''.join(b))
|
||||
type = line[32:36].strip()
|
||||
type = type.replace(" ", "")
|
||||
cen = line[18:21]
|
||||
data.append([int(cen)-1, type, a, b])
|
||||
except:
|
||||
pass
|
||||
return data
|
||||
|
||||
|
||||
def get_mol_orbital(data, centers):
|
||||
def inner_func(r):
|
||||
r = np.array(r)
|
||||
result = 0
|
||||
for dat in data:
|
||||
c_num, type, weight, exp = dat
|
||||
c = centers[c_num]
|
||||
R2center = (np.linalg.norm(r - c)) ** 2
|
||||
if type == 'S':
|
||||
result += weight * math.exp(-exp * R2center)
|
||||
elif type == 'X':
|
||||
result += weight * r[0] * math.exp(-exp * R2center)
|
||||
elif type == 'Y':
|
||||
result += weight * r[1] * math.exp(-exp * R2center)
|
||||
elif type == 'Z':
|
||||
result += weight * r[2] * math.exp(-exp * R2center)
|
||||
elif type == 'XX':
|
||||
result += weight * r[0] * r[0] * math.exp(-exp * R2center)
|
||||
elif type == 'YY':
|
||||
result += weight * r[1] * r[1] * math.exp(-exp * R2center)
|
||||
elif type == 'ZZ':
|
||||
result += weight * r[2] * r[2] * math.exp(-exp * R2center)
|
||||
elif type == 'XY':
|
||||
result += weight * r[0] * r[1] * math.exp(-exp * R2center)
|
||||
elif type == 'XZ':
|
||||
result += weight * r[0] * r[2] * math.exp(-exp * R2center)
|
||||
elif type == 'YZ':
|
||||
result += weight * r[1] * r[2] * math.exp(-exp * R2center)
|
||||
else:
|
||||
print('Error! This type is not described! type = ', type)
|
||||
return result
|
||||
return inner_func
|
||||
|
||||
|
||||
def orbitals(tip_type):
|
||||
|
||||
if tip_type == 'oxygen':
|
||||
O2_centers = [[0, 0, -1.188061], [0, 0, 1.188061]]
|
||||
O2_26_data = [[0, 'X', 1.59780706, 15.539616], [0, 'X', 1.23102421, 3.5999336],\
|
||||
[0, 'X', 0.540485934, 1.0137618], [0, 'X', 0.116494955, 0.27000582],\
|
||||
[1, 'X', -1.59780706, 15.539616], [1, 'X', -1.23102421, 3.5999336],\
|
||||
[1, 'X', -0.540485934, 1.0137618], [1, 'X', -0.116494955, 0.27000582]]
|
||||
O2_27_data = [[0, 'Y', 1.59780706, 15.539616], [0, 'Y', 1.23102421, 3.5999336],\
|
||||
[0, 'Y', 0.540485934, 1.0137618], [0, 'Y', 0.116494955, 0.27000582],\
|
||||
[1, 'Y', -1.59780706, 15.539616], [1, 'Y', -1.23102421, 3.5999336],\
|
||||
[1, 'Y', -0.540485934, 1.0137618], [1, 'Y', -0.116494955, 0.27000582]]
|
||||
O2_POMO_26 = get_mol_orbital(O2_26_data, O2_centers)
|
||||
O2_POMO_27 = get_mol_orbital(O2_27_data, O2_centers)
|
||||
return [O2_POMO_26, O2_POMO_27]
|
||||
|
||||
elif tip_type == 'IrCl6':
|
||||
IrCl6_centers = [[0.0, 0.0, 0.0], [0.0, 0.0, 4.791539], [0.0, 4.791539, 0.0], [-4.791539, 0.0, 0.0],\
|
||||
[0.0, 0.0, -4.791539], [0.0, -4.791539, 0.0], [4.791539, 0.0, 0.0]]
|
||||
IrCl6_29_data = [[0, 'YZ', 1.53018648, 1.24], [0, 'YZ', 0.316517948, 0.4647], [0, 'YZ', 0.0244957551, 0.1529],\
|
||||
[1, 'Y', 0.143821066, 6.296], [1, 'Y', -0.129983297, 0.6333], [1, 'Y', -0.0361555127, 0.1819],\
|
||||
[2, 'Z', 0.143821066, 6.296], [2, 'Z', -0.129983297, 0.6333], [2, 'Z', -0.0361555127, 0.1819],\
|
||||
[4, 'Y', -0.143821066, 6.296], [4, 'Y', 0.129983297, 0.6333], [4, 'Y', 0.0361555127, 0.1819],\
|
||||
[5, 'Z', -0.143821066, 6.296], [5, 'Z', 0.129983297, 0.6333], [5, 'Z', 0.0361555127, 0.1819]]
|
||||
IrCl6_30_data = [[0, 'XZ', 1.53018648, 1.24], [0, 'XZ', 0.316517948, 0.4647], [0, 'XZ', 0.0244957551, 0.1529],\
|
||||
[1, 'X', 0.143821066, 6.296], [1, 'X', -0.129983297, 0.6333], [1, 'X', -0.0361555127, 0.1819],\
|
||||
[3, 'Z', -0.143821066, 6.296], [3, 'Z', 0.129983297, 0.6333], [3, 'Z', 0.0361555127, 0.1819],\
|
||||
[4, 'X', -0.143821066, 6.296], [4, 'X', 0.129983297, 0.6333], [4, 'X', 0.0361555127, 0.1819],\
|
||||
[6, 'Z', 0.143821066, 6.296], [6, 'Z', -0.129983297, 0.6333], [6, 'Z', -0.0361555127, 0.1819]]
|
||||
IrCl6_31_data = [[0, 'XY', 1.53018648, 1.24], [0, 'XY', 0.316517948, 0.4647], [0, 'XY', 0.0244957551, 0.1529],\
|
||||
[2, 'X', 0.143821066, 6.296], [2, 'X', -0.129983297, 0.6333], [2, 'X', -0.0361555127, 0.1819],\
|
||||
[3, 'Y', -0.143821066, 6.296], [3, 'Y', 0.129983297, 0.6333], [3, 'Y', 0.0361555127, 0.1819],\
|
||||
[5, 'X', -0.143821066, 6.296], [5, 'X', 0.129983297, 0.6333], [5, 'X', 0.0361555127, 0.1819],\
|
||||
[6, 'Y', 0.143821066, 6.296], [6, 'Y', -0.129983297, 0.6333], [6, 'Y', -0.0361555127, 0.1819]]
|
||||
IrCl6_29 = get_mol_orbital(IrCl6_29_data, IrCl6_centers)
|
||||
IrCl6_30 = get_mol_orbital(IrCl6_30_data, IrCl6_centers)
|
||||
IrCl6_31 = get_mol_orbital(IrCl6_31_data, IrCl6_centers)
|
||||
return [IrCl6_29, IrCl6_30, IrCl6_31]
|
||||
|
||||
elif tip_type == 'RuNH3_6':
|
||||
RuNH3_6_centers = [[-1.73717655E-03, 2.23398325E-03, 5.95596999E-04], [2.80310681E+00, 1.05571952E+00, -2.92279419E+00],\
|
||||
[3.11970961E+00, -3.93834154E-01, -4.17275494E+00], [2.20095082E+00, 2.57871173E+00, -3.96265199E+00],\
|
||||
[2.86486865E+00, 6.10478919E-01, 2.99160626E+00], [2.39084949E+00, -2.98868166E-01, 4.63779676E+00],\
|
||||
[4.60991448E+00, -5.89373268E-02, 2.47213750E+00], [-1.16539862E+00, 4.01564374E+00, 3.25100126E-01],\
|
||||
[-1.81856108E+00, 4.42909277E+00, 2.10458406E+00], [3.14711646E-01, 5.21963568E+00, -3.05318533E-02],\
|
||||
[1.21468221E+00, -3.99589459E+00, -3.05565040E-01], [1.28176071E+00, -4.84983673E+00, 1.43473405E+00],\
|
||||
[2.84949113E-02, -5.05488013E+00, -1.41702627E+00], [-2.87292734E+00, -6.18033007E-01, -2.98146293E+00],\
|
||||
[-3.64380554E+00, -2.39428507E+00, -2.87082722E+00], [-4.34685477E+00, 6.34642890E-01, -2.85350843E+00],\
|
||||
[-2.83649579E+00, -1.07571542E+00, 2.88962265E+00], [-2.30623961E+00, -5.65363155E-01, 4.68553427E+00],\
|
||||
[-4.54655856E+00, -2.25539186E-01, 2.54576816E+00], [-3.15571140E+00, -2.98787230E+00, 2.91511808E+00],\
|
||||
[-2.12017233E+00, -4.36184326E-01, -4.75944808E+00], [2.98933039E+00, -4.14797331E+00, -1.07307692E+00],\
|
||||
[4.52142190E+00, 1.53602082E+00, -2.16060783E+00], [3.08027187E+00, 2.48844379E+00, 3.42328804E+00],\
|
||||
[-2.57792824E+00, 4.48333677E+00, -9.20281760E-01]]
|
||||
RuNH3_6_35_data = [[0, 'S', -5.90938527e-05, 1.508], [0, 'S', -6.69986041e-05, 0.5129], [0, 'S', 7.19232404e-05, 0.1362],\
|
||||
[0, 'S', 0.000667825176, 2.565], [0, 'S', -0.00057234006, 1.508], [0, 'S', -0.000107459153, 0.5129],\
|
||||
[0, 'S', 3.48252636e-05, 0.0417], [0, 'X', -0.000852926268, 4.859], [0, 'X', 0.00119048253, 1.219],\
|
||||
[0, 'X', 0.000164935069, 0.4413], [0, 'Y', -6.78621664e-05, 4.859], [0, 'Y', 9.47194692e-05, 1.219],\
|
||||
[0, 'Y', 1.31228824e-05, 0.4413], [0, 'Z', 0.000373774035, 4.859], [0, 'Z', -0.000521699794, 1.219],\
|
||||
[0, 'Z', -7.22787521e-05, 0.4413], [0, 'X', -0.000230489569, 0.5725], [0, 'X', 0.000240730695, 0.083],\
|
||||
[0, 'Y', 0.000424795214, 0.5725], [0, 'Y', -0.000443669739, 0.083], [0, 'Z', -0.000188882268, 0.5725],\
|
||||
[0, 'Z', 0.000197274696, 0.083], [0, 'X', -1.04499827e-05, 0.025], [0, 'Y', 2.0331508e-05, 0.025],\
|
||||
[0, 'Z', -8.67202565e-06, 0.025], [0, 'XX', -0.295461307, 4.195], [0, 'XX', -0.442053419, 1.377],\
|
||||
[0, 'XX', -0.0792638374, 0.4828], [0, 'YY', -0.0672194557, 4.195], [0, 'YY', -0.100570158, 1.377],\
|
||||
[0, 'YY', -0.0180330618, 0.4828], [0, 'ZZ', 0.362680763, 4.195], [0, 'ZZ', 0.542623577, 1.377],\
|
||||
[0, 'ZZ', 0.0972968992, 0.4828], [0, 'XY', 0.0475038166, 4.195], [0, 'XY', 0.071072672, 1.377],\
|
||||
[0, 'XY', 0.0127439184, 0.4828], [0, 'XZ', -0.360816255, 4.195], [0, 'XZ', -0.539834, 1.377],\
|
||||
[0, 'XZ', -0.0967967049, 0.4828], [0, 'YZ', 1.21237734, 4.195], [0, 'YZ', 1.81389419, 1.377],\
|
||||
[0, 'YZ', 0.325246244, 0.4828], [0, 'XX', -0.00640014621, 0.1501], [0, 'YY', -0.0015460197, 0.1501],\
|
||||
[0, 'ZZ', 0.00794616591, 0.1501], [0, 'XY', 0.000762713575, 0.1501], [0, 'XZ', -0.0078740316, 0.1501],\
|
||||
[0, 'YZ', 0.0264934952, 0.1501], [1, 'S', 8.84456152e-05, 5909.0], [1, 'S', 0.000163160035, 887.5],\
|
||||
[1, 'S', 0.000263459435, 204.7], [1, 'S', 0.000357001585, 59.84], [1, 'S', 0.000372065476, 20.0],\
|
||||
[1, 'S', 0.000216286771, 7.193], [1, 'S', 3.36894315e-05, 2.686], [1, 'S', 0.000104934209, 7.193],\
|
||||
[1, 'S', -0.000120618546, 0.7], [1, 'S', -0.000271470039, 0.2133], [1, 'X', 0.00260803484, 26.79],\
|
||||
[1, 'X', 0.0025401732, 5.956], [1, 'X', 0.00178461332, 1.707], [1, 'X', 0.00067760306, 0.5314],\
|
||||
[1, 'Y', 0.0525694372, 26.79], [1, 'Y', 0.0512015687, 5.956], [1, 'Y', 0.0359719573, 1.707],\
|
||||
[1, 'Y', 0.0136582575, 0.5314], [1, 'Z', 0.0190111972, 26.79], [1, 'Z', 0.0185165217, 5.956],\
|
||||
[1, 'Z', 0.013008889, 1.707], [1, 'Z', 0.0049393686, 0.5314], [1, 'X', -0.000755895033, 0.1654],\
|
||||
[1, 'Y', 0.00215785904, 0.1654], [1, 'Z', -4.18680836e-06, 0.1654], [2, 'S', -0.00652499257, 19.2406],\
|
||||
[2, 'S', -0.0111143259, 2.8992], [2, 'S', -0.0128500417, 0.6534], [2, 'S', -0.00736568257, 0.1776],\
|
||||
[3, 'S', 0.00283887518, 19.2406], [3, 'S', 0.00483558925, 2.8992], [3, 'S', 0.00559075956, 0.6534],\
|
||||
[3, 'S', 0.0019287417, 0.1776], [4, 'S', -4.25579402e-05, 5909.0], [4, 'S', -7.85087534e-05, 887.5],\
|
||||
[4, 'S', -0.000126770455, 204.7], [4, 'S', -0.000171780727, 59.84], [4, 'S', -0.000179029116, 20.0],\
|
||||
[4, 'S', -0.000104072084, 7.193], [4, 'S', -1.62105584e-05, 2.686], [4, 'S', -2.38121517e-05, 7.193],\
|
||||
[4, 'S', 2.73713134e-05, 0.7], [4, 'S', 0.000279429803, 0.2133], [4, 'X', 0.0263895084, 26.79],\
|
||||
[4, 'X', 0.0257028475, 5.956], [4, 'X', 0.0180576837, 1.707], [4, 'X', 0.00685635457, 0.5314],\
|
||||
[4, 'Y', -0.0427381048, 26.79], [4, 'Y', -0.0416260498, 5.956], [4, 'Y', -0.0292446213, 1.707],\
|
||||
[4, 'Y', -0.0111039431, 0.5314], [4, 'Z', -0.0163681686, 26.79], [4, 'Z', -0.0159422652, 5.956],\
|
||||
[4, 'Z', -0.0112003303, 1.707], [4, 'Z', -0.00425267367, 0.5314], [4, 'X', -0.000588236873, 0.1654],\
|
||||
[4, 'Y', 0.000383927762, 0.1654], [4, 'Z', 0.000887710294, 0.1654], [5, 'S', -0.00112688506, 19.2406],\
|
||||
[5, 'S', -0.00191947617, 2.8992], [5, 'S', -0.00221923931, 0.6534], [5, 'S', -0.00251590545, 0.1776],\
|
||||
[6, 'S', 0.00787639039, 19.2406], [6, 'S', 0.0134162252, 2.8992], [6, 'S', 0.0155114269, 0.6534],\
|
||||
[6, 'S', 0.0117906265, 0.1776], [7, 'S', 0.000290643176, 5909.0], [7, 'S', 0.000536163953, 887.5],\
|
||||
[7, 'S', 0.000865760125, 204.7], [7, 'S', 0.00117315114, 59.84], [7, 'S', 0.00122265294, 20.0],\
|
||||
[7, 'S', 0.000710744947, 7.193], [7, 'S', 0.000110707618, 2.686], [7, 'S', 0.000580000402, 7.193],\
|
||||
[7, 'S', -0.000666692074, 0.7], [7, 'S', -0.000791507988, 0.2133], [7, 'X', -0.00114263758, 26.79],\
|
||||
[7, 'X', -0.00111290589, 5.956], [7, 'X', -0.000781878451, 1.707], [7, 'X', -0.000296872842, 0.5314],\
|
||||
[7, 'Y', 0.00554095051, 26.79], [7, 'Y', 0.00539677374, 5.956], [7, 'Y', 0.00379153451, 1.707],\
|
||||
[7, 'Y', 0.00143961459, 0.5314], [7, 'Z', -0.0664334707, 26.79], [7, 'Z', -0.064704857, 5.956],\
|
||||
[7, 'Z', -0.0454587704, 1.707], [7, 'Z', -0.0172603227, 0.5314], [7, 'X', 0.000922022693, 0.1654],\
|
||||
[7, 'Y', -0.000104102274, 0.1654], [7, 'Z', -0.000908609724, 0.1654], [8, 'S', -0.00838784083, 19.2406],\
|
||||
[8, 'S', -0.0142874027, 2.8992], [8, 'S', -0.0165186555, 0.6534], [8, 'S', -0.0103740236, 0.1776],\
|
||||
[9, 'S', 0.00116448404, 19.2406], [9, 'S', 0.00198352029, 2.8992],[9, 'S', 0.00229328515, 0.6534],\
|
||||
[9, 'S', 0.000746855298, 0.1776], [10, 'S', -0.000159483962, 5909.0], [10, 'S', -0.000294208013, 887.5],\
|
||||
[10, 'S', -0.000475066562, 204.7], [10, 'S', -0.000643740529, 59.84], [10, 'S', -0.000670903538, 20.0],\
|
||||
[10, 'S', -0.00039000544, 7.193], [10, 'S', -6.07483366e-05, 2.686], [10, 'S', -0.000287196538, 7.193],\
|
||||
[10, 'S', 0.000330123316, 0.7], [10, 'S', 0.00050688431, 0.2133], [10, 'X', 0.0112645738, 26.79],\
|
||||
[10, 'X', 0.0109714671, 5.956], [10, 'X', 0.00770806744, 1.707], [10, 'X', 0.00292669006, 0.5314],\
|
||||
[10, 'Y', -0.000944376614, 26.79], [10, 'Y', -0.000919803725, 5.956], [10, 'Y', -0.00064621341, 1.707],\
|
||||
[10, 'Y', -0.000245361938, 0.5314], [10, 'Z', 0.0596286316, 26.79], [10, 'Z', 0.0580770813, 5.956],\
|
||||
[10, 'Z', 0.0408023883, 1.707], [10, 'Z', 0.0154923326, 0.5314], [10, 'X', 0.000209517748, 0.1654],\
|
||||
[10, 'Y', -0.000360297199, 0.1654], [10, 'Z', -0.000498649525, 0.1654], [11, 'S', 0.00915046467, 19.2406],\
|
||||
[11, 'S', 0.0155864157, 2.8992], [11, 'S', 0.0180205344, 0.6534], [11, 'S', 0.0129436385, 0.1776],\
|
||||
[12, 'S', -0.00660194617, 19.2406], [12, 'S', -0.0112454046, 2.8992], [12, 'S', -0.0130015909, 0.6534],\
|
||||
[12, 'S', -0.00966746285, 0.1776], [13, 'S', 7.83519574e-05, 5909.0], [13, 'S', 0.000144539761, 887.5],\
|
||||
[13, 'S', 0.000233392717, 204.7], [13, 'S', 0.000316259579, 59.84], [13, 'S', 0.000329604337, 20.0],\
|
||||
[13, 'S', 0.000191603528, 7.193], [13, 'S', 2.98447006e-05, 2.686], [13, 'S', 0.000246799817, 7.193],\
|
||||
[13, 'S', -0.000283688566, 0.7], [13, 'S', -6.68862239e-05, 0.2133], [13, 'X', -0.0305463365, 26.79],\
|
||||
[13, 'X', -0.0297515141, 5.956], [13, 'X', -0.0209020977, 1.707], [13, 'X', -0.00793635528, 0.5314],\
|
||||
[13, 'Y', 0.0315627487, 26.79], [13, 'Y', 0.0307414789, 5.956], [13, 'Y', 0.0215976033, 1.707],\
|
||||
[13, 'Y', 0.00820043303, 0.5314], [13, 'Z', 0.0231505973, 26.79], [13, 'Z', 0.0225482136, 5.956],\
|
||||
[13, 'Z', 0.0158413775, 1.707], [13, 'Z', 0.00601484126, 0.5314], [13, 'X', 0.000237693867, 0.1654],\
|
||||
[13, 'Y', -0.00123192073, 0.1654], [13, 'Z', 0.000288092875, 0.1654], [14, 'S', -0.00402881913, 19.2406],\
|
||||
[14, 'S', -0.00686247659, 2.8992], [14, 'S', -0.00793418435, 0.6534], [14, 'S', -0.00693878318, 0.1776],\
|
||||
[15, 'S', 0.00845874161, 19.2406], [15, 'S', 0.0144081713, 2.8992], [15, 'S', 0.0166582845, 0.6534],\
|
||||
[15, 'S', 0.0131334049, 0.1776], [16, 'S', -0.000169669121, 5909.0], [16, 'S', -0.000312997083, 887.5],\
|
||||
[16, 'S', -0.000505405842, 204.7], [16, 'S', -0.000684851871, 59.84], [16, 'S', -0.000713749597, 20.0],\
|
||||
[16, 'S', -0.000414912443, 7.193], [16, 'S', -6.46279209e-05, 2.686], [16, 'S', -0.000409625509, 7.193],\
|
||||
[16, 'S', 0.000470851536, 0.7], [16, 'S', 0.000158871955, 0.2133], [16, 'X', -0.0106805032, 26.79],\
|
||||
[16, 'X', -0.0104025942, 5.956], [16, 'X', -0.00730840251, 1.707], [16, 'X', -0.00277494056, 0.5314],\
|
||||
[16, 'Y', -0.0345284702, 26.79], [16, 'Y', -0.0336300317, 5.956], [16, 'Y', -0.0236269727, 1.707],\
|
||||
[16, 'Y', -0.00897096797, 0.5314], [16, 'Z', -0.0277495771, 26.79], [16, 'Z', -0.0270275269, 5.956],\
|
||||
[16, 'Z', -0.0189883449, 1.707], [16, 'Z', -0.007209719, 0.5314], [16, 'X', -0.000866107884, 0.1654],\
|
||||
[16, 'Y', 0.000791526817, 0.1654], [16, 'Z', -0.000995436732, 0.1654], [17, 'S', -0.00496446317, 19.2406],\
|
||||
[17, 'S', -0.00845620296, 2.8992], [17, 'S', -0.00977680176, 0.6534], [17, 'S', -0.00611518732, 0.1776],\
|
||||
[18, 'S', -0.00155862643, 19.2406], [18, 'S', -0.0026548815, 2.8992], [18, 'S', -0.00306949233, 0.6534],\
|
||||
[18, 'S', -0.00339973617, 0.1776], [19, 'S', 0.00651256261, 19.2406], [19, 'S', 0.0110931534, 2.8992],\
|
||||
[19, 'S', 0.0128255627, 0.6534], [19, 'S', 0.00960542363, 0.1776], [20, 'S', -0.00442680693, 19.2406],\
|
||||
[20, 'S', -0.00754038788, 2.8992], [20, 'S', -0.00871796453, 0.6534], [20, 'S', -0.00582235994, 0.1776],\
|
||||
[21, 'S', -0.00271809997, 19.2406], [21, 'S', -0.00462986716, 2.8992], [21, 'S', -0.00535291, 0.6534],\
|
||||
[21, 'S', -0.00443674741, 0.1776], [22, 'S', 0.00382464248, 19.2406], [22, 'S', 0.00651469293, 2.8992],\
|
||||
[22, 'S', 0.00753208757, 0.6534], [22, 'S', 0.00564203493, 0.1776], [23, 'S', -0.00685678285, 19.2406],\
|
||||
[23, 'S', -0.0116794798, 2.8992], [23, 'S', -0.0135034553, 0.6534], [23, 'S', -0.0100430099, 0.1776],\
|
||||
[24, 'S', 0.00744861736, 19.2406], [24, 'S', 0.0126875793, 2.8992], [24, 'S', 0.0146689889, 0.6534],\
|
||||
[24, 'S', 0.0111030771, 0.1776]]
|
||||
RuNH3_6_36_data = [[0, 'S', 5.56219117e-05, 1.508], [0, 'S', 6.30622352e-05, 0.5129], [0, 'S', -6.76975344e-05, 0.1362],\
|
||||
[0, 'S', -0.000531100526, 2.565], [0, 'S', 0.000455164192, 1.508], [0, 'S', 8.54589118e-05, 0.5129],\
|
||||
[0, 'S', -4.21526227e-05, 0.0417], [0, 'X', 0.00055829555, 4.859], [0, 'X', -0.00077924801, 1.219],\
|
||||
[0, 'X', -0.000107960698, 0.4413], [0, 'Y', -0.000233384918, 4.859], [0, 'Y', 0.000325749924, 1.219],\
|
||||
[0, 'Y', 4.51309322e-05, 0.4413], [0, 'Z', -0.000209188811, 4.859], [0, 'Z', 0.000291977904, 1.219],\
|
||||
[0, 'Z', 4.04519971e-05, 0.4413], [0, 'X', -0.000662019566, 0.5725], [0, 'X', 0.000691434456, 0.083],\
|
||||
[0, 'Y', 0.00029307311, 0.5725], [0, 'Y', -0.00030609495, 0.083], [0, 'Z', 0.000292702989, 0.5725],\
|
||||
[0, 'Z', -0.000305708385, 0.083], [0, 'X', -2.76526194e-05, 0.025], [0, 'Y', 1.48379268e-05, 0.025],\
|
||||
[0, 'Z', 2.0610484e-05, 0.025], [0, 'XX', 0.31450656, 4.195], [0, 'XX', 0.470547909, 1.377],\
|
||||
[0, 'XX', 0.0843731353, 0.4828], [0, 'YY', 0.229594293, 4.195], [0, 'YY', 0.343506713, 1.377],\
|
||||
[0, 'YY', 0.0615935972, 0.4828], [0, 'ZZ', -0.544100853, 4.195], [0, 'ZZ', -0.814054622, 1.377],\
|
||||
[0, 'ZZ', -0.145966732, 0.4828], [0, 'XY', 0.97411606, 4.195], [0, 'XY', 1.4574204, 1.377],\
|
||||
[0, 'XY', 0.261327542, 0.4828], [0, 'XZ', -0.0405782461, 4.195], [0, 'XZ', -0.0607110035, 1.377],\
|
||||
[0, 'XZ', -0.0108859855, 0.4828], [0, 'YZ', 0.454156155, 4.195], [0, 'YZ', 0.679484171, 1.377],\
|
||||
[0, 'YZ', 0.121837137, 0.4828], [0, 'XX', 0.00679643964, 0.1501], [0, 'YY', 0.00510300971, 0.1501],\
|
||||
[0, 'ZZ', -0.0118994494, 0.1501], [0, 'XY', 0.0213597971, 0.1501], [0, 'XZ', -0.000875545312, 0.1501],\
|
||||
[0, 'YZ', 0.0101456122, 0.1501], [1, 'S', -1.09192934e-05, 5909.0], [1, 'S', -2.01433647e-05, 887.5],\
|
||||
[1, 'S', -3.25260993e-05, 204.7], [1, 'S', -4.40745993e-05, 59.84], [1, 'S', -4.59343528e-05, 20.0],\
|
||||
[1, 'S', -2.67022701e-05, 7.193], [1, 'S', -4.15922017e-06, 2.686], [1, 'S', -5.50655737e-05, 7.193],\
|
||||
[1, 'S', 6.32961312e-05, 0.7], [1, 'S', -9.03633726e-05, 0.2133], [1, 'X', -0.0384295808, 26.79],\
|
||||
[1, 'X', -0.0374296346, 5.956], [1, 'X', -0.0262964056, 1.707], [1, 'X', -0.00998452978, 0.5314],\
|
||||
[1, 'Y', -0.0131017406, 26.79], [1, 'Y', -0.0127608304, 5.956], [1, 'Y', -0.00896519497, 1.707],\
|
||||
[1, 'Y', -0.00340401109, 0.5314], [1, 'Z', -0.0385472955, 26.79], [1, 'Z', -0.0375442863, 5.956],\
|
||||
[1, 'Z', -0.0263769548, 1.707], [1, 'Z', -0.0100151137, 0.5314], [1, 'X', -0.000651252471, 0.1654],\
|
||||
[1, 'Y', 0.00153747643, 0.1654], [1, 'Z', -0.000247277414, 0.1654], [2, 'S', 0.00582315223, 19.2406],\
|
||||
[2, 'S', 0.00991884832, 2.8992], [2, 'S', 0.0114678673, 0.6534], [2, 'S', 0.00948207442, 0.1776],\
|
||||
[3, 'S', 0.00214294491, 19.2406], [3, 'S', 0.00365017859, 2.8992], [3, 'S', 0.0042202242, 0.6534],\
|
||||
[3, 'S', 0.00147162814, 0.1776], [4, 'S', -6.82029859e-05, 5909.0], [4, 'S', -0.000125817447, 887.5],\
|
||||
[4, 'S', -0.000203161231, 204.7], [4, 'S', -0.000275294304, 59.84], [4, 'S', -0.000286910508, 20.0],\
|
||||
[4, 'S', -0.000166785019, 7.193], [4, 'S', -2.59789003e-05, 2.686], [4, 'S', -0.000209550435, 7.193],\
|
||||
[4, 'S', 0.000240871582, 0.7], [4, 'S', 3.90585007e-05, 0.2133], [4, 'X', -0.0222719157, 26.79],\
|
||||
[4, 'X', -0.0216923955, 5.956], [4, 'X', -0.0152401175, 1.707], [4, 'X', -0.00578654779, 0.5314],\
|
||||
[4, 'Y', -0.0526912426, 26.79], [4, 'Y', -0.0513202047, 5.956], [4, 'Y', -0.0360553057, 1.707],\
|
||||
[4, 'Y', -0.0136899042, 0.5314], [4, 'Z', 0.0304810868, 26.79], [4, 'Z', 0.0296879621, 5.956],\
|
||||
[4, 'Z', 0.0208574489, 1.707], [4, 'Z', 0.0079194025, 0.5314], [4, 'X', 0.000167167683, 0.1654],\
|
||||
[4, 'Y', -0.000251508023, 0.1654], [4, 'Z', -1.83135036e-05, 0.1654], [5, 'S', 0.00944561796, 19.2406],\
|
||||
[5, 'S', 0.0160891641, 2.8992], [5, 'S', 0.0186017966, 0.6534], [5, 'S', 0.0134597372, 0.1776],\
|
||||
[6, 'S', -0.00237882245, 19.2406], [6, 'S', -0.00405195985, 2.8992], [6, 'S', -0.00468475134, 0.6534],\
|
||||
[6, 'S', -0.00406723053, 0.1776], [7, 'S', 1.23113753e-05, 5909.0], [7, 'S', 2.27114077e-05, 887.5],\
|
||||
[7, 'S', 3.66727958e-05, 204.7], [7, 'S', 4.96935943e-05, 59.84], [7, 'S', 5.17904446e-05, 20.0],\
|
||||
[7, 'S', 3.01064967e-05, 7.193], [7, 'S', 4.68947201e-06, 2.686], [7, 'S', 3.27892042e-06, 7.193],\
|
||||
[7, 'S', -3.76901507e-06, 0.7], [7, 'S', -3.21084903e-06, 0.2133], [7, 'X', -0.0453716649, 26.79],\
|
||||
[7, 'X', -0.0441910841, 5.956], [7, 'X', -0.0310467009, 1.707], [7, 'X', -0.011788178, 0.5314],\
|
||||
[7, 'Y', -0.0105021073, 26.79], [7, 'Y', -0.0102288401, 5.956], [7, 'Y', -0.00718633057, 1.707],\
|
||||
[7, 'Y', -0.00272859085, 0.5314], [7, 'Z', -0.00940613787, 26.79], [7, 'Z', -0.00916138807, 5.956],\
|
||||
[7, 'Z', -0.00643638601, 1.707], [7, 'Z', -0.00244384304, 0.5314], [7, 'X', -0.00186778818, 0.1654],\
|
||||
[7, 'Y', -0.000712057951, 0.1654], [7, 'Z', 0.00113608073, 0.1654], [8, 'S', -0.00100831405, 19.2406],\
|
||||
[8, 'S', -0.00171750862, 2.8992], [8, 'S', -0.00198573063, 0.6534], [8, 'S', -0.00299073334, 0.1776],\
|
||||
[9, 'S', -0.0044447342, 19.2406], [9, 'S', -0.00757092424, 2.8992], [9, 'S', -0.00875326973, 0.6534],\
|
||||
[9, 'S', -0.00423832627, 0.1776], [10, 'S', -0.000457245237, 5909.0], [10, 'S', -0.000843503078, 887.5],\
|
||||
[10, 'S', -0.00136202989, 204.7], [10, 'S', -0.00184562314, 59.84], [10, 'S', -0.00192350029, 20.0],\
|
||||
[10, 'S', -0.00111815714, 7.193], [10, 'S', -0.000174167279, 2.686], [10, 'S', -0.00104676381, 7.193],\
|
||||
[10, 'S', 0.00120322182, 0.7], [10, 'S', 0.000895658494, 0.2133], [10, 'X', 0.0293098929, 26.79],\
|
||||
[10, 'X', 0.028547243, 5.956], [10, 'X', 0.0200560302, 1.707], [10, 'X', 0.00761511035, 0.5314],\
|
||||
[10, 'Y', 0.00944318459, 26.79], [10, 'Y', 0.00919747083, 5.956], [10, 'Y', 0.00646173616, 1.707],\
|
||||
[10, 'Y', 0.00245346828, 0.5314], [10, 'Z', 0.0281989298, 26.79], [10, 'Z', 0.0274651874, 5.956],\
|
||||
[10, 'Z', 0.0192958257, 1.707], [10, 'Z', 0.00732646696, 0.5314], [10, 'X', -0.00057701544, 0.1654],\
|
||||
[10, 'Y', -0.00063761305, 0.1654], [10, 'Z', 0.00163521154, 0.1654], [11, 'S', 0.00172354654, 19.2406],\
|
||||
[11, 'S', 0.00293579767, 2.8992], [11, 'S', 0.00339427896, 0.6534], [11, 'S', 0.000364705439, 0.1776],\
|
||||
[12, 'S', -0.00587728837, 19.2406], [12, 'S', -0.0100110609, 2.8992], [12, 'S', -0.0115744807, 0.6534],\
|
||||
[12, 'S', -0.00840107361, 0.1776], [13, 'S', 0.000115874039, 5909.0], [13, 'S', 0.000213758615, 887.5],\
|
||||
[13, 'S', 0.00034516249, 204.7], [13, 'S', 0.000467713583, 59.84], [13, 'S', 0.000487449032, 20.0],\
|
||||
[13, 'S', 0.000283360817, 7.193], [13, 'S', 4.41370722e-05, 2.686], [13, 'S', 0.000129872941, 7.193],\
|
||||
[13, 'S', -0.000149284829, 0.7], [13, 'S', -0.000476787014, 0.2133], [13, 'X', 0.0269341081, 26.79],\
|
||||
[13, 'X', 0.0262332766, 5.956], [13, 'X', 0.0184303397, 1.707], [13, 'X', 0.00699784902, 0.5314],\
|
||||
[13, 'Y', 0.0519580248, 26.79], [13, 'Y', 0.0506060655, 5.956], [13, 'Y', 0.0355535831, 1.707],\
|
||||
[13, 'Y', 0.0134994042, 0.5314], [13, 'Z', -0.0383878092, 26.79], [13, 'Z', -0.0373889499, 5.956],\
|
||||
[13, 'Z', -0.0262678223, 1.707], [13, 'Z', -0.00997367694, 0.5314], [13, 'X', -2.67012554e-05, 0.1654],\
|
||||
[13, 'Y', 0.000904200797, 0.1654], [13, 'Z', 5.09914983e-05, 0.1654], [14, 'S', -0.00912908749, 19.2406],\
|
||||
[14, 'S', -0.0155500029, 2.8992], [14, 'S', -0.017978435, 0.6534], [14, 'S', -0.0120460603, 0.1776],\
|
||||
[15, 'S', 0.0016055472, 19.2406], [15, 'S', 0.00273480385, 2.8992], [15, 'S', 0.00316189609, 0.6534],\
|
||||
[15, 'S', 0.00209218298, 0.1776], [16, 'S', 0.00056041656, 5909.0], [16, 'S', 0.00103382836, 887.5],\
|
||||
[16, 'S', 0.00166935387, 204.7], [16, 'S', 0.00226206353, 59.84], [16, 'S', 0.00235751262, 20.0],\
|
||||
[16, 'S', 0.00137045446, 7.193], [16, 'S', 0.000213465815, 2.686], [16, 'S', 0.00123513498, 7.193],\
|
||||
[16, 'S', -0.00141974851, 0.7], [16, 'S', -0.00113528459, 0.2133], [16, 'X', 0.0251517247, 26.79],\
|
||||
[16, 'X', 0.0244972713, 5.956], [16, 'X', 0.0172106992, 1.707], [16, 'X', 0.00653476148, 0.5314],\
|
||||
[16, 'Y', 0.0249511719, 26.79], [16, 'Y', 0.0243019368, 5.956], [16, 'Y', 0.0170734658, 1.707],\
|
||||
[16, 'Y', 0.00648265511, 0.5314], [16, 'Z', 0.0387229602, 26.79], [16, 'Z', 0.0377153802, 5.956],\
|
||||
[16, 'Z', 0.0264971578, 1.707], [16, 'Z', 0.0100607537, 0.5314], [16, 'X', -0.000516896597, 0.1654],\
|
||||
[16, 'Y', 0.000704840718, 0.1654], [16, 'Z', -0.000725627747, 0.1654], [17, 'S', 0.00856706598, 19.2406],\
|
||||
[17, 'S', 0.0145926853, 2.8992], [17, 'S', 0.0168716139, 0.6534], [17, 'S', 0.0131577385, 0.1776],\
|
||||
[18, 'S', -0.00424743898, 19.2406], [18, 'S', -0.00723486203, 2.8992], [18, 'S', -0.00836472495, 0.6534],\
|
||||
[18, 'S', -0.00641875413, 0.1776], [19, 'S', -0.00404193473, 19.2406], [19, 'S', -0.006884817, 2.8992],\
|
||||
[19, 'S', -0.00796001365, 0.6534], [19, 'S', -0.0048262046, 0.1776], [20, 'S', 0.00765131699, 19.2406],\
|
||||
[20, 'S', 0.0130328471, 2.8992], [20, 'S', 0.0150681769, 0.6534], [20, 'S', 0.0107127476, 0.1776],\
|
||||
[21, 'S', 0.00394124012, 19.2406], [21, 'S', 0.00671329916, 2.8992], [21, 'S', 0.00776171, 0.6534],\
|
||||
[21, 'S', 0.00637671339, 0.1776], [22, 'S', -0.0080669874, 19.2406], [22, 'S', -0.013740878, 2.8992],\
|
||||
[22, 'S', -0.0158867805, 0.6534], [22, 'S', -0.0112089415, 0.1776], [23, 'S', -0.00703771987, 19.2406],\
|
||||
[23, 'S', -0.0119876783, 2.8992], [23, 'S', -0.013859785, 0.6534], [23, 'S', -0.00968297661, 0.1776],\
|
||||
[24, 'S', 0.00557469287, 19.2406], [24, 'S', 0.00949563585, 2.8992], [24, 'S', 0.0109785621, 0.6534],\
|
||||
[24, 'S', 0.00747695987, 0.1776]]
|
||||
RuNH3_6_37_data = [[0, 'S', 6.46572809e-05, 1.508], [0, 'S', 7.33062301e-05, 0.5129], [0, 'S', -7.86944994e-05, 0.1362],\
|
||||
[0, 'S', -0.000705332604, 2.565], [0, 'S', 0.000604484705, 1.508], [0, 'S', 0.00011349444, 0.5129],\
|
||||
[0, 'S', -5.48831361e-05, 0.0417], [0, 'X', -0.00067295965, 4.859], [0, 'X', 0.000939291866, 1.219],\
|
||||
[0, 'X', 0.000130133929, 0.4413], [0, 'Y', 0.000629472821, 4.859], [0, 'Y', -0.000878594579, 1.219],\
|
||||
[0, 'Y', -0.00012172464, 0.4413], [0, 'Z', 0.000910094893, 4.859], [0, 'Z', -0.00127027635, 1.219],\
|
||||
[0, 'Z', -0.000175990082, 0.4413], [0, 'X', -0.000236679238, 0.5725], [0, 'X', 0.000247195383, 0.083],\
|
||||
[0, 'Y', -0.000540292625, 0.5725], [0, 'Y', 0.000564298937, 0.083], [0, 'Z', 0.000564723999, 0.5725],\
|
||||
[0, 'Z', -0.000589815847, 0.083], [0, 'X', -1.10741654e-05, 0.025], [0, 'Y', -2.85595029e-05, 0.025],\
|
||||
[0, 'Z', 3.006976e-05, 0.025], [0, 'XX', 0.631922415, 4.195], [0, 'XX', 0.94544855, 1.377],\
|
||||
[0, 'XX', 0.169526751, 0.4828], [0, 'YY', -0.328395499, 4.195], [0, 'YY', -0.491327797, 1.377],\
|
||||
[0, 'YY', -0.0880991412, 0.4828], [0, 'ZZ', -0.303526916, 4.195], [0, 'ZZ', -0.454120753, 1.377],\
|
||||
[0, 'ZZ', -0.0814276102, 0.4828], [0, 'XY', -0.799007062, 4.195], [0, 'XY', -1.19543167, 1.377],\
|
||||
[0, 'XY', -0.214350794, 0.4828], [0, 'XZ', -0.201426236, 4.195], [0, 'XZ', -0.301363172, 1.377],\
|
||||
[0, 'XZ', -0.0540369113, 0.4828], [0, 'YZ', 0.42420367, 4.195], [0, 'YZ', 0.634670864, 1.377],\
|
||||
[0, 'YZ', 0.11380174, 0.4828], [0, 'XX', 0.0140554236, 0.1501], [0, 'YY', -0.0072542984, 0.1501],\
|
||||
[0, 'ZZ', -0.00680112517, 0.1501], [0, 'XY', -0.017662346, 0.1501], [0, 'XZ', -0.00442890466, 0.1501],\
|
||||
[0, 'YZ', 0.00925994787, 0.1501], [1, 'S', -0.000144334932, 5909.0], [1, 'S', -0.000266261843, 887.5],\
|
||||
[1, 'S', -0.000429941038, 204.7], [1, 'S', -0.000582593037, 59.84], [1, 'S', -0.000607175891, 20.0],\
|
||||
[1, 'S', -0.000352959684, 7.193], [1, 'S', -5.49779862e-05, 2.686], [1, 'S', -0.000458434382, 7.193],\
|
||||
[1, 'S', 0.000526955788, 0.7], [1, 'S', 0.000175791862, 0.2133], [1, 'X', -0.0348575911, 26.79],\
|
||||
[1, 'X', -0.0339505889, 5.956], [1, 'X', -0.023852182, 1.707], [1, 'X', -0.00905647809, 0.5314],\
|
||||
[1, 'Y', 0.041745699, 26.79], [1, 'Y', 0.0406594666, 5.956], [1, 'Y', 0.0285655427, 1.707],\
|
||||
[1, 'Y', 0.0108461026, 0.5314], [1, 'Z', -0.0194515659, 26.79], [1, 'Z', -0.0189454318, 5.956],\
|
||||
[1, 'Z', -0.0133102224, 1.707], [1, 'Z', -0.00505378239, 0.5314], [1, 'X', 0.000364263504, 0.1654],\
|
||||
[1, 'Y', -0.000219696323, 0.1654], [1, 'Z', -0.000642960835, 0.1654], [2, 'S', -0.00472926304, 19.2406],\
|
||||
[2, 'S', -0.00805557555, 2.8992], [2, 'S', -0.00931360867, 0.6534], [2, 'S', -0.00755418466, 0.1776],\
|
||||
[3, 'S', 0.00921480538, 19.2406], [3, 'S', 0.0156960102, 2.8992], [3, 'S', 0.0181472442, 0.6534],\
|
||||
[3, 'S', 0.012974182, 0.1776], [4, 'S', -1.64347564e-05, 5909.0], [4, 'S', -3.03180143e-05, 887.5],\
|
||||
[4, 'S', -4.89554132e-05, 204.7], [4, 'S', -6.63371959e-05, 59.84], [4, 'S', -6.91363327e-05, 20.0],\
|
||||
[4, 'S', -4.01898997e-05, 7.193], [4, 'S', -6.26009102e-06, 2.686], [4, 'S', -5.20037054e-05, 7.193],\
|
||||
[4, 'S', 5.97766106e-05, 0.7], [4, 'S', -2.91336866e-05, 0.2133], [4, 'X', -0.0356018561, 26.79],\
|
||||
[4, 'X', -0.0346754879, 5.956], [4, 'X', -0.024361464, 1.707], [4, 'X', -0.00924984829, 0.5314],\
|
||||
[4, 'Y', 0.0194700911, 26.79], [4, 'Y', 0.018963475, 5.956], [4, 'Y', 0.0133228987, 1.707],\
|
||||
[4, 'Y', 0.00505859549, 0.5314], [4, 'Z', 0.0298093313, 26.79], [4, 'Z', 0.0290336859, 5.956],\
|
||||
[4, 'Z', 0.0203977834, 1.707], [4, 'Z', 0.00774487125, 0.5314], [4, 'X', -0.00181624823, 0.1654],\
|
||||
[4, 'Y', 0.000367426852, 0.1654], [4, 'Z', 0.00140796861, 0.1654], [5, 'S', 0.00289171111, 19.2406],\
|
||||
[5, 'S', 0.00492558716, 2.8992], [5, 'S', 0.00569481236, 0.6534], [5, 'S', 0.00293981612, 0.1776],\
|
||||
[6, 'S', -0.00565730622, 19.2406], [6, 'S', -0.00963635504, 2.8992], [6, 'S', -0.0111412573, 0.6534],\
|
||||
[6, 'S', -0.00601759579, 0.1776], [7, 'S', -0.000210847953, 5909.0], [7, 'S', -0.000388961728, 887.5],\
|
||||
[7, 'S', -0.000628068248, 204.7], [7, 'S', -0.000851065974, 59.84], [7, 'S', -0.000886977201, 20.0],\
|
||||
[7, 'S', -0.000515612028, 7.193], [7, 'S', -8.03131695e-05, 2.686], [7, 'S', -0.000492953053, 7.193],\
|
||||
[7, 'S', 0.000566633906, 0.7], [7, 'S', 0.000291422681, 0.2133], [7, 'X', 0.0493648046, 26.79],\
|
||||
[7, 'X', 0.0480803214, 5.956], [7, 'X', 0.033779107, 1.707], [7, 'X', 0.0128256502, 0.5314],\
|
||||
[7, 'Y', 0.0125337243, 26.79], [7, 'Y', 0.012207594, 5.956], [7, 'Y', 0.00857651553, 1.707],\
|
||||
[7, 'Y', 0.00325643269, 0.5314], [7, 'Z', -0.0185032854, 26.79], [7, 'Z', -0.0180218258, 5.956],\
|
||||
[7, 'Z', -0.0126613376, 1.707], [7, 'Z', -0.00480740616, 0.5314], [7, 'X', -0.000499509519, 0.1654],\
|
||||
[7, 'Y', -0.000173911673, 0.1654], [7, 'Z', 0.00047173766, 0.1654], [8, 'S', -0.00538615124, 19.2406],\
|
||||
[8, 'S', -0.00917448403, 2.8992], [8, 'S', -0.0106072562, 0.6534], [8, 'S', -0.00807752979, 0.1776],\
|
||||
[9, 'S', 0.00881402738, 19.2406], [9, 'S', 0.0150133462, 2.8992], [9, 'S', 0.0173579691, 0.6534],\
|
||||
[9, 'S', 0.0129760346, 0.1776], [10, 'S', 0.000211422777, 5909.0], [10, 'S', 0.000390022135, 887.5],\
|
||||
[10, 'S', 0.000629780518, 204.7], [10, 'S', 0.000853386191, 59.84], [10, 'S', 0.000889395322, 20.0],\
|
||||
[10, 'S', 0.000517017715, 7.193], [10, 'S', 8.05321232e-05, 2.686], [10, 'S', 0.000563489539, 7.193],\
|
||||
[10, 'S', -0.00064771336, 0.7], [10, 'S', -0.000133561341, 0.2133], [10, 'X', -0.0549054694, 26.79],\
|
||||
[10, 'X', -0.0534768168, 5.956], [10, 'X', -0.037570446, 1.707], [10, 'X', -0.0142651906, 0.5314],\
|
||||
[10, 'Y', -0.0225286335, 26.79], [10, 'Y', -0.0219424334, 5.956], [10, 'Y', -0.0154157831, 1.707],\
|
||||
[10, 'Y', -0.00585324655, 0.5314], [10, 'Z', 0.0241438107, 26.79], [10, 'Z', 0.0235155834, 5.956],\
|
||||
[10, 'Z', 0.0165210087, 1.707], [10, 'Z', 0.00627289166, 0.5314], [10, 'X', -0.00083675402, 0.1654],\
|
||||
[10, 'Y', -0.000482939566, 0.1654], [10, 'Z', 0.0014981449, 0.1654], [11, 'S', 0.00348617662, 19.2406],\
|
||||
[11, 'S', 0.00593816816, 2.8992], [11, 'S', 0.00686552737, 0.6534], [11, 'S', 0.0038172112, 0.1776],\
|
||||
[12, 'S', 0.00515560836, 19.2406], [12, 'S', 0.00878178954, 2.8992], [12, 'S', 0.010153235, 0.6534],\
|
||||
[12, 'S', 0.00750694743, 0.1776], [13, 'S', -5.49524391e-05, 5909.0], [13, 'S', -0.000101373503, 887.5],\
|
||||
[13, 'S', -0.000163690857, 204.7], [13, 'S', -0.000221809841, 59.84], [13, 'S', -0.000231169237, 20.0],\
|
||||
[13, 'S', -0.000134381852, 7.193], [13, 'S', -2.09316927e-05, 2.686], [13, 'S', -0.000245504263, 7.193],\
|
||||
[13, 'S', 0.000282199367, 0.7], [13, 'S', -8.95241297e-05, 0.2133], [13, 'X', 0.0296979085, 26.79],\
|
||||
[13, 'X', 0.0289251623, 5.956], [13, 'X', 0.0203215396, 1.707], [13, 'X', 0.00771592209, 0.5314],\
|
||||
[13, 'Y', -0.0229310905, 26.79], [13, 'Y', -0.0223344184, 5.956], [13, 'Y', -0.0156911744, 1.707],\
|
||||
[13, 'Y', -0.00595781038, 0.5314], [13, 'Z', -0.0240680915, 26.79], [13, 'Z', -0.0234418344, 5.956],\
|
||||
[13, 'Z', -0.0164691959, 1.707], [13, 'Z', -0.00625321876, 0.5314], [13, 'X', 0.000248203201, 0.1654],\
|
||||
[13, 'Y', -0.00195105995, 0.1654], [13, 'Z', 4.95942547e-05, 0.1654], [14, 'S', -6.3651167e-05, 19.2406],\
|
||||
[14, 'S', -0.000108420018, 2.8992], [14, 'S', -0.00012535189, 0.6534], [14, 'S', -0.00142203548, 0.1776],\
|
||||
[15, 'S', -0.0050424763, 19.2406], [15, 'S', -0.0085890864, 2.8992], [15, 'S', -0.0099304375, 0.6534],\
|
||||
[15, 'S', -0.00595550103, 0.1776], [16, 'S', 0.000175383692, 5909.0], [16, 'S', 0.000323539038, 887.5],\
|
||||
[16, 'S', 0.000522428254, 204.7], [16, 'S', 0.000707918148, 59.84], [16, 'S', 0.000737789169, 20.0],\
|
||||
[16, 'S', 0.000428886976, 7.193], [16, 'S', 6.68046333e-05, 2.686], [16, 'S', 0.000476517161, 7.193],\
|
||||
[16, 'S', -0.000547741369, 0.7], [16, 'S', -0.000331187193, 0.2133], [16, 'X', 0.0423760874, 26.79],\
|
||||
[16, 'X', 0.0412734522, 5.956], [16, 'X', 0.0289969018, 1.707], [16, 'X', 0.0110098861, 0.5314],\
|
||||
[16, 'Y', -0.0526163557, 26.79], [16, 'Y', -0.0512472664, 5.956], [16, 'Y', -0.0360040625, 1.707],\
|
||||
[16, 'Y', -0.0136704476, 0.5314], [16, 'Z', 0.0217360893, 26.79], [16, 'Z', 0.0211705114, 5.956],\
|
||||
[16, 'Z', 0.0148734648, 1.707], [16, 'Z', 0.00564733276, 0.5314], [16, 'X', 0.0015961123, 0.1654],\
|
||||
[16, 'Y', -0.000999996156, 0.1654], [16, 'Z', 0.000321058399, 0.1654], [17, 'S', 0.00241879545, 19.2406],\
|
||||
[17, 'S', 0.00412004775, 2.8992], [17, 'S', 0.00476347248, 0.6534], [17, 'S', 0.00311018499, 0.1776],\
|
||||
[18, 'S', -0.0088536234, 19.2406], [18, 'S', -0.015080792, 2.8992], [18, 'S', -0.0174359479, 0.6534],\
|
||||
[18, 'S', -0.0104878557, 0.1776], [19, 'S', 0.00653506894, 19.2406], [19, 'S', 0.0111314894, 2.8992],\
|
||||
[19, 'S', 0.0128698857, 0.6534], [19, 'S', 0.00880857322, 0.1776], [20, 'S', 0.00514585371, 19.2406],\
|
||||
[20, 'S', 0.00876517399, 2.8992], [20, 'S', 0.0101340246, 0.6534], [20, 'S', 0.00725308796, 0.1776],\
|
||||
[21, 'S', -0.0085865535, 19.2406], [21, 'S', -0.0146258793, 2.8992], [21, 'S', -0.0169099918, 0.6534],\
|
||||
[21, 'S', -0.0110080324, 0.1776], [22, 'S', -0.00458878599, 19.2406], [22, 'S', -0.00781629442, 2.8992],\
|
||||
[22, 'S', -0.00903695918, 0.6534], [22, 'S', -0.00673431556, 0.1776], [23, 'S', 0.00282027644, 19.2406],\
|
||||
[23, 'S', 0.00480390914, 2.8992], [23, 'S', 0.00555413198, 0.6534], [23, 'S', 0.00344027048, 0.1776],\
|
||||
[24, 'S', -0.00359574202, 19.2406], [24, 'S', -0.00612479603, 2.8992], [24, 'S', -0.00708130078, 0.6534],\
|
||||
[24, 'S', -0.00539612557, 0.1776]]
|
||||
RuNH36_35 = get_mol_orbital(RuNH3_6_35_data, RuNH3_6_centers)
|
||||
RuNH36_36 = get_mol_orbital(RuNH3_6_36_data, RuNH3_6_centers)
|
||||
RuNH36_37 = get_mol_orbital(RuNH3_6_37_data, RuNH3_6_centers)
|
||||
return [RuNH36_35, RuNH36_36, RuNH36_37]
|
||||
else:
|
||||
pass
|
||||
Reference in New Issue
Block a user