Source code for PyCD.io

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

from datetime import datetime

import numpy as np


[docs]def read_poscar(input_file_path): """ This function reads in a VASP-style POSCAR file and returns dict containing a variety of POSCAR-derived quantities. Parameters ---------- file_location : str The location of the POSCAR file to read in Returns ------- poscar_info : dict of {str : np.ndarray, str : list, str : list, str : int, str : str, str : np.ndarray, str : str} Dict of POSCAR-derived quantities """ input_file = open(input_file_path, 'r') element_types_line_number = 6 for line_index, line in enumerate(input_file): line_number = line_index + 1 if line_number == element_types_line_number: prospective_element_types = line[:-1].split() element_types_exist = prospective_element_types[0].isalpha() break input_file.close() lattice_matrix = np.zeros((3, 3)) lattice_parameter_index = 0 lattice_parameters_line_range = range(3, 6) element_types_line_number = 6 * element_types_exist num_elements_line_number = 6 + element_types_exist coordinate_type_number = 7 + element_types_exist coord_start_line_number = 8 + element_types_exist input_file = open(input_file_path, 'r') for line_index, line in enumerate(input_file): line_number = line_index + 1 if line_number == 1 and not element_types_exist: element_types = line.split() elif line_number in lattice_parameters_line_range: lattice_matrix[lattice_parameter_index, :] = ( np.fromstring(line, sep=' ')) lattice_parameter_index += 1 elif (line_number == element_types_line_number and element_types_exist): element_types = line.split() elif line_number == num_elements_line_number: num_elements = np.fromstring(line, dtype=int, sep=' ') total_elements = num_elements.sum() coordinates = np.zeros((total_elements, 3)) elif line_number == coordinate_type_number: coordinate_type = line.split()[0] elif line_number == coord_start_line_number: element_index = 0 coordinates[element_index, :] = np.fromstring(line, sep=' ') coordinate_string_length = len(line.split()[0]) if coordinate_string_length == 18: file_format = 'VASP' elif coordinate_string_length == 11: file_format = 'VESTA' else: file_format = 'unknown' elif ((line_number > coord_start_line_number) and (element_index < total_elements - 1)): element_index += 1 coordinates[element_index, :] = np.fromstring(line, sep=' ') input_file.close() poscar_info_keys = {'lattice_matrix', 'element_types', 'num_elements', 'total_elements', 'coordinate_type', 'coordinates', 'file_format'} poscar_info = {} for key in poscar_info_keys: poscar_info[key] = locals()[key] return poscar_info
[docs]def write_poscar(src_file_path, dst_file_path, file_format, element_types_cluster, num_elements_cluster, coordinate_type, coordinates_cluster): """ This function writes out a VASP-style POSCAR file and returns dict containing a variety of POSCAR-derived quantities. Parameters ---------- src_file_path : str The location of the source file to read in dst_file_path : str The location of the destination file to write out file_format : str The file format of the source coordinate file: VASP, VESTA, unknown element_types_cluster : list List of element types in the cluster num_elements_cluster : list List containing number of elements for each element type listed in element_types_cluster coordinate_type : str Nature of coordinates : Cartesian or Fractional coordinates_cluster : np.ndarray NumPy array containing the coordinates within the selected cluster Returns ------- None : NoneType """ unmodified_line_number_limit = 5 src_file = open(src_file_path, 'r') open(dst_file_path, 'w').close() dst_file = open(dst_file_path, 'a') for line_index, line in enumerate(src_file): line_number = line_index + 1 if line_number <= unmodified_line_number_limit: dst_file.write(line) else: break src_file.close() element_types_line = (' ' * 3 + (' ' * 4).join(element_types_cluster) + '\n') dst_file.write(element_types_line) num_elements_line = ( ' ' * 3 + (' ' * 4).join(map(str, num_elements_cluster)) + '\n') dst_file.write(num_elements_line) dst_file.write(coordinate_type + '\n') for element_coordinates in coordinates_cluster: if file_format == 'VASP' or file_format == 'unknown': line = ( ''.join([ ' ' * 1, [' ', '-'][element_coordinates[0] < 0], f'{abs(element_coordinates[0]):18.16f}', ' ' * 1, [' ', '-'][element_coordinates[1] < 0], f'{abs(element_coordinates[1]):18.16f}', ' ' * 1, [' ', '-'][element_coordinates[2] < 0], f'{abs(element_coordinates[2]):18.16f}']) + '\n') elif file_format == 'VESTA': line = ( ''.join([ ' ' * 4, [' ', '-'][element_coordinates[0] < 0], f'{abs(element_coordinates[0]):11.9f}', ' ' * 8, [' ', '-'][element_coordinates[1] < 0], f'{abs(element_coordinates[1]):11.9f}', ' ' * 8, [' ', '-'][element_coordinates[2] < 0], f'{abs(element_coordinates[2]):11.9f}']) + '\n') dst_file.write(line) dst_file.close() return None
[docs]def generate_report(start_time, dst_path, file_name, print_time_elapsed, prefix=None): """ Generates a report file to the output directory This function writes out a VASP-style POSCAR file and returns dict containing a variety of POSCAR-derived quantities. Parameters ---------- start_time : datetime.datetime Time value when this function is called dst_path : str The location of the destination file to write out file_name : str The file name of the output report print_time_elapsed : bool Flag value to whether to print elapsed time or not prefix : str or NoneType String value to prefix the newly generated report content Returns ------- None : NoneType """ report_file_name = file_name + '.log' report_file_path = dst_path / report_file_name report = open(report_file_path, 'w') end_time = datetime.now() time_elapsed = end_time - start_time if prefix: report.write(prefix) if print_time_elapsed: report.write('Time elapsed: ' + ('%2d days, ' % time_elapsed.days if time_elapsed.days else '') + ('%2d hours' % ((time_elapsed.seconds // 3600) % 24)) + (', %2d minutes' % ((time_elapsed.seconds // 60) % 60)) + (', %2d seconds' % (time_elapsed.seconds % 60))) report.close() return None