Add POSCAR writer and str generator
This commit is contained in:
parent
8aa8164a80
commit
61cc0343e0
1 changed files with 213 additions and 0 deletions
213
nafuma/dft/io.py
213
nafuma/dft/io.py
|
|
@ -2,6 +2,10 @@ import pandas as pd
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
import linecache
|
import linecache
|
||||||
|
|
||||||
import nafuma.auxillary as aux
|
import nafuma.auxillary as aux
|
||||||
|
|
@ -543,3 +547,212 @@ def get_doscar_information(path):
|
||||||
|
|
||||||
#def get_bader_charges(poscar='POSCAR', acf='ACF.dat'):
|
#def get_bader_charges(poscar='POSCAR', acf='ACF.dat'):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def write_poscar(data: dict, options={}):
|
||||||
|
|
||||||
|
required_options = ['path', 'overwrite', 'scale']
|
||||||
|
|
||||||
|
default_options = {
|
||||||
|
'path': './POSCAR.vasp',
|
||||||
|
'overwrite': False,
|
||||||
|
'scale': 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
options = aux.update_options(options=options, required_options=required_options, default_options=default_options)
|
||||||
|
|
||||||
|
|
||||||
|
if os.path.isfile(options['path']) and not options['overwrite']:
|
||||||
|
warnings.warn(f"File {options['path']} already exists, and overwrite disabled. Quitting.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
atom_nums = count_atoms(data)
|
||||||
|
|
||||||
|
with open(options['path'], 'w') as fout:
|
||||||
|
|
||||||
|
# Write first line
|
||||||
|
for atom in data['atoms']:
|
||||||
|
if atom_nums[atom] > 1:
|
||||||
|
fout.write(f'{atom}{atom_nums[atom]}')
|
||||||
|
else:
|
||||||
|
fout.write(f'{atom}')
|
||||||
|
|
||||||
|
fout.write('\t - Automatically generated by the NAFUMA Python package.\n')
|
||||||
|
|
||||||
|
# Write second line
|
||||||
|
fout.write(str(options['scale'])+'\n')
|
||||||
|
|
||||||
|
# Write lattice parameters
|
||||||
|
# FIXME Now only writes cells without any angles
|
||||||
|
fout.write('\t{:<09} \t {:<09} \t {:<09}\n'.format(
|
||||||
|
str(data['lattice_parameters'][0]),
|
||||||
|
str(0.0),
|
||||||
|
str(0.0),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fout.write('\t{:<09} \t {:<09} \t {:<09}\n'.format(
|
||||||
|
str(0.0),
|
||||||
|
str(data['lattice_parameters'][1]),
|
||||||
|
str(0.0),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fout.write('\t{:<09} \t {:<09} \t {:<09}\n'.format(
|
||||||
|
str(0.0),
|
||||||
|
str(0.0),
|
||||||
|
str(data['lattice_parameters'][2]),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Write atoms
|
||||||
|
for atom in data['atoms']:
|
||||||
|
fout.write(f'\t{atom}')
|
||||||
|
fout.write('\n')
|
||||||
|
|
||||||
|
for atom in data['atoms']:
|
||||||
|
fout.write(f'\t{atom_nums[atom]}')
|
||||||
|
fout.write('\n')
|
||||||
|
|
||||||
|
fout.write('Direct\n')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for atom in data['atoms']:
|
||||||
|
for label, coords in data['coordinates'].items():
|
||||||
|
if atom in label:
|
||||||
|
fout.write('\t{:<09} \t {:<09} \t {:<09}\n'.format(
|
||||||
|
coords[0],
|
||||||
|
coords[1],
|
||||||
|
coords[2]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def apply_transformation(data, rotation=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], translation=[0,0,0]):
|
||||||
|
|
||||||
|
if not isinstance(rotation, np.ndarray):
|
||||||
|
rotation = np.array(rotation)
|
||||||
|
|
||||||
|
if not rotation.shape == (3,3):
|
||||||
|
print("NOOOO!!!!")
|
||||||
|
|
||||||
|
for label in data['coordinates'].keys():
|
||||||
|
data['coordinates'][label] = rotation.dot(data['coordinates'][label])
|
||||||
|
data['coordinates'][label] = translate_back(data['coordinates'][label])
|
||||||
|
data['coordinates'][label] = data['coordinates'][label] + translation
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def translate_back(coords):
|
||||||
|
|
||||||
|
for i, coord in enumerate(coords):
|
||||||
|
if coord < 0:
|
||||||
|
while coords[i] < 0:
|
||||||
|
coords[i] = coords[i]+1
|
||||||
|
|
||||||
|
elif coord >= 1:
|
||||||
|
while coords[i] >= 1:
|
||||||
|
coords[i] = coords[i]-1
|
||||||
|
|
||||||
|
return coords
|
||||||
|
|
||||||
|
|
||||||
|
def count_atoms(data):
|
||||||
|
atom_nums = {}
|
||||||
|
for atom in data['atoms']:
|
||||||
|
atom_nums[atom] = 0
|
||||||
|
|
||||||
|
for label in data['coordinates'].keys():
|
||||||
|
for atom in data['atoms']:
|
||||||
|
if atom in label:
|
||||||
|
atom_nums[atom] += 1
|
||||||
|
|
||||||
|
|
||||||
|
return atom_nums
|
||||||
|
|
||||||
|
def append_data(data, new_data):
|
||||||
|
|
||||||
|
if not new_data:
|
||||||
|
return data
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
data = {
|
||||||
|
'atoms': new_data['atoms'],
|
||||||
|
'coordinates': {},
|
||||||
|
'lattice_parameters': new_data['lattice_parameters']
|
||||||
|
}
|
||||||
|
|
||||||
|
atom_num = count_atoms(data)
|
||||||
|
|
||||||
|
new_coords = {}
|
||||||
|
|
||||||
|
for label, coords in data['coordinates'].items():
|
||||||
|
new_coords[label] = coords
|
||||||
|
|
||||||
|
|
||||||
|
extend_unit_cell = [0,0,0]
|
||||||
|
for label, coords in new_data['coordinates'].items():
|
||||||
|
atom = ''.join(filter(str.isalpha, label))
|
||||||
|
number = int(''.join(filter(str.isnumeric, label)))
|
||||||
|
new_number = number + atom_num[atom]
|
||||||
|
new_label = atom+str(new_number)
|
||||||
|
|
||||||
|
new_coords[new_label] = coords
|
||||||
|
|
||||||
|
for i, coord in enumerate(coords):
|
||||||
|
if coord > 1 and np.floor(coord) >= extend_unit_cell[i]:
|
||||||
|
extend_unit_cell[i] = np.floor(coord)
|
||||||
|
|
||||||
|
data['coordinates'] = new_coords
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def make_supercell(data, supercell):
|
||||||
|
|
||||||
|
for i, param in enumerate(data['lattice_parameters']):
|
||||||
|
data['lattice_parameters'][i] = supercell[i] * param
|
||||||
|
|
||||||
|
if supercell[i] > 0:
|
||||||
|
|
||||||
|
for label, coords in data['coordinates'].items():
|
||||||
|
data['coordinates'][label][i] = (1/supercell[i])*data['coordinates'][label][i]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def copy_data(data):
|
||||||
|
|
||||||
|
new_data = {}
|
||||||
|
|
||||||
|
new_data['atoms'] = list(data['atoms'])
|
||||||
|
new_data['coordinates'] = data['coordinates'].copy()
|
||||||
|
new_data['lattice_parameters'] = list(data['lattice_parameters'])
|
||||||
|
|
||||||
|
return new_data
|
||||||
|
|
||||||
|
|
||||||
|
def unit_vector(vector):
|
||||||
|
""" Returns the unit vector of the vector. """
|
||||||
|
return vector / np.linalg.norm(vector)
|
||||||
|
|
||||||
|
def angle_between(v1, v2):
|
||||||
|
""" Returns the angle in radians between vectors 'v1' and 'v2'::
|
||||||
|
|
||||||
|
>>> angle_between((1, 0, 0), (0, 1, 0))
|
||||||
|
1.5707963267948966
|
||||||
|
>>> angle_between((1, 0, 0), (1, 0, 0))
|
||||||
|
0.0
|
||||||
|
>>> angle_between((1, 0, 0), (-1, 0, 0))
|
||||||
|
3.141592653589793
|
||||||
|
"""
|
||||||
|
v1_u = unit_vector(v1)
|
||||||
|
v2_u = unit_vector(v2)
|
||||||
|
return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
|
||||||
Loading…
Add table
Add a link
Reference in a new issue