init
This commit is contained in:
233
electrochemistry/echem/io_data/ddec.py
Normal file
233
electrochemistry/echem/io_data/ddec.py
Normal file
@@ -0,0 +1,233 @@
|
||||
from matplotlib import colors
|
||||
import numpy as np
|
||||
from monty.re import regrep
|
||||
from echem.core.structure import Structure
|
||||
from nptyping import NDArray, Shape, Number
|
||||
from dataclasses import dataclass
|
||||
from echem.core.constants import ElemNum2Name, Bohr2Angstrom
|
||||
|
||||
|
||||
@dataclass()
|
||||
class LocalMultipoleMoments:
|
||||
net_charges: NDArray[Shape['Natoms'], Number]
|
||||
dipoles: NDArray[Shape['Natoms, 4'], Number]
|
||||
quadrupoles: NDArray[Shape['Natoms, 8'], Number]
|
||||
|
||||
|
||||
class Output_DDEC:
|
||||
def __init__(self,
|
||||
structure: Structure,
|
||||
lmm_hirshfeld: LocalMultipoleMoments,
|
||||
lmm_ddec: LocalMultipoleMoments,
|
||||
charges_cm5: NDArray[Shape['Natons'], Number]):
|
||||
|
||||
self.structure = structure
|
||||
self.lmm_hirshfeld = lmm_hirshfeld
|
||||
self.lmm_ddec = lmm_ddec
|
||||
self.charges_cm5 = charges_cm5
|
||||
|
||||
@staticmethod
|
||||
def _process_lmm_(data, line_number, natoms):
|
||||
charges_ddec = np.zeros(natoms)
|
||||
dipoles_ddec = np.zeros((natoms, 4))
|
||||
quadrupoles_ddec = np.zeros((natoms, 8))
|
||||
|
||||
idx = 0
|
||||
while len(line := data[line_number].split()) != 0:
|
||||
charges_ddec[idx] = float(line[5])
|
||||
dipoles_ddec[idx] = list(map(float, line[6: 10]))
|
||||
quadrupoles_ddec[idx] = list(map(float, line[10:]))
|
||||
|
||||
line_number += 1
|
||||
idx += 1
|
||||
|
||||
return LocalMultipoleMoments(charges_ddec, dipoles_ddec, quadrupoles_ddec)
|
||||
|
||||
@staticmethod
|
||||
def from_file(filepath):
|
||||
file = open(filepath, 'r')
|
||||
data = file.readlines()
|
||||
file.close()
|
||||
|
||||
patterns = {'lattice': r' parameters',
|
||||
'lmm': r'Multipole analysis for each of the expansion sites.',
|
||||
'cm5': r'The computed CM5 net atomic charges are:'}
|
||||
matches = regrep(filepath, patterns)
|
||||
|
||||
lattice = np.zeros((3, 3))
|
||||
|
||||
i = matches['lattice'][0][1]
|
||||
natoms = int((data[i + 1].split()[0]).split('.')[0])
|
||||
|
||||
line = data[i + 2].split()
|
||||
NX = int(line[0].split('.')[0])
|
||||
lattice[0] = np.array([float(line[1]), float(line[2]), float(line[3])])
|
||||
line = data[i + 3].split()
|
||||
NY = int(line[0].split('.')[0])
|
||||
lattice[1] = np.array([float(line[1]), float(line[2]), float(line[3])])
|
||||
line = data[i + 4].split()
|
||||
NZ = int(line[0].split('.')[0])
|
||||
lattice[2] = np.array([float(line[1]), float(line[2]), float(line[3])])
|
||||
|
||||
if NX > 0 and NY > 0 and NZ > 0:
|
||||
units = 'Bohr'
|
||||
elif NX < 0 and NY < 0 and NZ < 0:
|
||||
units = 'Angstrom'
|
||||
else:
|
||||
raise ValueError('The sign of the number of all voxels should be > 0 or < 0')
|
||||
|
||||
if units == 'Angstrom':
|
||||
NX, NY, NZ = -NX, -NY, -NZ
|
||||
|
||||
lattice = lattice * np.array([NX, NY, NZ]).reshape((-1, 1)) * Bohr2Angstrom
|
||||
|
||||
coords = np.zeros((natoms, 3))
|
||||
species = []
|
||||
line_number = matches['lmm'][0][1] + 3
|
||||
idx = 0
|
||||
while len(line := data[line_number].split()) != 0:
|
||||
species.append(ElemNum2Name[int(line[1])])
|
||||
coords[idx] = list(map(float, line[2:5]))
|
||||
line_number += 1
|
||||
|
||||
structure = Structure(lattice, species, coords)
|
||||
|
||||
line_number = matches['lmm'][0][1] + 3
|
||||
lmm_hirshfeld = Output_DDEC._process_lmm_(data, line_number, natoms)
|
||||
line_number = matches['lmm'][1][1] + 3
|
||||
lmm_ddec = Output_DDEC._process_lmm_(data, line_number, natoms)
|
||||
|
||||
line_number = matches['cm5'][0][1] + 1
|
||||
charges_cm5 = []
|
||||
i = 0
|
||||
while i < natoms:
|
||||
charges = list(map(float, data[line_number].split()))
|
||||
charges_cm5 += charges
|
||||
line_number += 1
|
||||
i += len(charges)
|
||||
|
||||
return Output_DDEC(structure, lmm_hirshfeld, lmm_ddec, np.array(charges_cm5))
|
||||
|
||||
|
||||
class AtomicNetCharges:
|
||||
"""Class that operates with DDEC output file DDEC6_even_tempered_net_atomic_charges.xyz"""
|
||||
def __init__(self, structure: Structure, net_charges, dipoles_xyz=None,
|
||||
dipoles_mag=None, Qs=None, quadrupole_tensor_eigs=None, date=None):
|
||||
"""
|
||||
Create a DDEC class object.
|
||||
Args:
|
||||
structure (Structure class): a base class that contains lattice, coords and species information
|
||||
net_charges:
|
||||
dipoles_xyz:
|
||||
dipoles_mag:
|
||||
Qs:
|
||||
quadrupole_tensor_eigs:
|
||||
"""
|
||||
self.structure = structure
|
||||
self.net_charges = net_charges
|
||||
self.dipoles_xyz = dipoles_xyz
|
||||
self.dipoles_mag = dipoles_mag
|
||||
self.Qs = Qs
|
||||
self.quadrupole_tensor_eigs = quadrupole_tensor_eigs
|
||||
self.date = date
|
||||
|
||||
@staticmethod
|
||||
def from_file(filepath):
|
||||
"""
|
||||
Read the positions of atoms and theirs charges
|
||||
from file "DDEC6_even_tempered_net_atomic_charges.xyz"
|
||||
|
||||
Parameters:
|
||||
----------
|
||||
filepath: str
|
||||
Path to file with atomic charges
|
||||
|
||||
Returns:
|
||||
-------
|
||||
DDEC class instance
|
||||
"""
|
||||
file = open(filepath, 'r')
|
||||
data = file.readlines()
|
||||
file.close()
|
||||
|
||||
patterns = {'date': r'\s+(\d\d\d\d/\d\d/\d\d\s+\d\d:\d\d:\d\d)'}
|
||||
matches = regrep(filepath, patterns)
|
||||
date = matches['date'][0][0][0]
|
||||
|
||||
natoms = int(data[0])
|
||||
x_axis = data[1].split()[10:13]
|
||||
y_axis = data[1].split()[15:18]
|
||||
z_axis = data[1].split()[20:23]
|
||||
lattice = np.array([x_axis, y_axis, z_axis], dtype=np.float32)
|
||||
|
||||
for start_line, string in enumerate(data):
|
||||
if 'The following XYZ coordinates are in angstroms' in string:
|
||||
break
|
||||
|
||||
coords = np.zeros((natoms, 3))
|
||||
species = []
|
||||
net_charges = np.zeros(natoms)
|
||||
dipoles_xyz = np.zeros((natoms, 3))
|
||||
dipoles_mag = np.zeros(natoms)
|
||||
Qs = np.zeros((natoms, 5))
|
||||
quadrupole_tensor_eigs = np.zeros((natoms, 3))
|
||||
|
||||
for i, j in enumerate(range(start_line + 2, start_line + 2 + natoms)):
|
||||
line_splitted = data[j].split()
|
||||
species.append(line_splitted[1])
|
||||
coords[i] = line_splitted[2:5]
|
||||
net_charges[i] = line_splitted[5]
|
||||
dipoles_xyz[i] = line_splitted[6:9]
|
||||
dipoles_mag[i] = line_splitted[9]
|
||||
Qs[i] = line_splitted[10:15]
|
||||
quadrupole_tensor_eigs[i] = line_splitted[15:18]
|
||||
|
||||
structure = Structure(lattice, species, coords, coords_are_cartesian=True)
|
||||
return AtomicNetCharges(structure, net_charges, dipoles_xyz, dipoles_mag, Qs, quadrupole_tensor_eigs, date)
|
||||
|
||||
|
||||
class AtomicSpinMoments:
|
||||
"""Class that operates with DDEC output file DDEC6_even_tempered_atomic_spin_moments.xyz"""
|
||||
def __init__(self, structure: Structure, spin_moments, date):
|
||||
self.structure = structure
|
||||
self.spin_moments = spin_moments
|
||||
self.date = date
|
||||
|
||||
@staticmethod
|
||||
def from_file(filepath):
|
||||
file = open(filepath, 'r')
|
||||
data = file.readlines()
|
||||
file.close()
|
||||
|
||||
patterns = {'date': r'\s+(\d\d\d\d/\d\d/\d\d\s+\d\d:\d\d:\d\d)'}
|
||||
matches = regrep(filepath, patterns)
|
||||
date = matches['date'][0][0][0]
|
||||
|
||||
natoms = int(data[0])
|
||||
x_axis = data[1].split()[10:13]
|
||||
y_axis = data[1].split()[15:18]
|
||||
z_axis = data[1].split()[20:23]
|
||||
lattice = np.array([x_axis, y_axis, z_axis], dtype=np.float32)
|
||||
|
||||
coords = np.zeros((natoms, 3))
|
||||
species = []
|
||||
spin_moments = np.zeros(natoms)
|
||||
|
||||
for i, j in enumerate(range(2, 2 + natoms)):
|
||||
line_splitted = data[j].split()
|
||||
species += [line_splitted[0]]
|
||||
coords[i] = line_splitted[1:4]
|
||||
spin_moments[i] = line_splitted[4]
|
||||
|
||||
structure = Structure(lattice, species, coords, coords_are_cartesian=True)
|
||||
return AtomicSpinMoments(structure, spin_moments, date)
|
||||
|
||||
|
||||
class MidpointNormalize(colors.Normalize):
|
||||
def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False):
|
||||
self.midpoint = midpoint
|
||||
colors.Normalize.__init__(self, vmin, vmax, clip)
|
||||
|
||||
def __call__(self, value, clip=None):
|
||||
x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1]
|
||||
return np.ma.masked_array(np.interp(value, x, y))
|
||||
Reference in New Issue
Block a user