Source code for bicrystallography

import numpy as np

[docs] class bicrystallography: """ Class to calculate grain boundary (GB) properties based on bicrystallography. Attributes: sigma (int): Sigma value representing the coincidence site lattice (CSL) density. misorientation (float): Misorientation angle between grains (in degrees). inclination (float): Inclination angle of the grain boundary (in degrees). axis (np.ndarray): Rotation axis vector (3D). lattice_parameter (float): Lattice parameter of the crystal. """
[docs] def __init__(self,sigma,misorientation,inclination,axis,lattice_parameter): """ Initialize a bicrystallography object. Args: sigma (int): Sigma value representing the CSL density. misorientation (float): Misorientation angle (degrees). inclination (float): Inclination angle of the GB (degrees). axis (list or np.ndarray): Rotation axis vector. lattice_parameter (float): Lattice parameter of the crystal. """ self.sigma = sigma self.misorientation = misorientation self.inclination = inclination self.axis = axis self.lattice_parameter = lattice_parameter
@staticmethod def _read_data(path): """ Read and parse grain boundary data from a text file (OILAB output format). Args: path (str): Path to the GB data file. Returns: list: A list of parsed rows, where each row contains GB properties. """ i = 0 j = -1 data = [] k = 0 with open(path, 'r') as file: flag = 0 for line in file: fields = line.split(' ') i = i + 1 if (len(fields) == 3 and fields[0] == "Sigma"): j = -1 flag = 1 row = [] sig = (float(fields[2])) if (fields[0] == "Misorientation="): n = len(fields) mis = (float(fields[n - 1])) if (len(fields) > 15): nrow = [] if fields[2] != "Inclination" and fields[1] != "-------------": flag = 2 for t in range(len(fields)): if fields[t] != "": j = j + 1 nrow.append(fields[t]) if flag == 2: row = [sig, mis, float(nrow[0]), float(nrow[7]), float(nrow[1]), float(nrow[2]), float(nrow[3]),float(nrow[4]),float(nrow[5]), float(nrow[6]), float(nrow[8]), float(nrow[9]), float(nrow[10]),float(nrow[14]), float(nrow[16]),float(nrow[17]), float(nrow[18]), float(nrow[22]), float(nrow[23]),float(nrow[24]), float(nrow[25]), float(nrow[29]), float(nrow[15]), float(nrow[11]),float(nrow[12]), float(nrow[13])] data.append(row) return data def _find_ATGB_data(self,path,period_cutoff=50): """ Filter GB data based on misorientation and period constraints. Args: path (str): Path to the OILAB output file. period_cutoff (float, optional): Maximum allowed CSL period. Defaults to 50. Returns: np.ndarray: Filtered and sorted GB data for acceptable ATGB configurations. """ mis = self.misorientation axis = self.axis # Read file data = np.asarray(self._read_data(path)) # Append gbs based on period_cutoff atgb_data = [] prev_inc = -10 for j in range(len(data)): if abs(data[j, 1] - mis) < 0.2 and abs(data[j, 2] - prev_inc) > 3 and data[j, 3] < period_cutoff: atgb_data.append( [data[j, 0], data[j, 1], data[j, 2], data[j, 3], data[j, 10], data[j, 11], data[j, 12], data[j, 13], data[j, 22], data[j, 23], data[j, 24], data[j, 25]]) prev_inc = data[j, 2] if abs(data[j, 1] - mis) < 0.2 and data[j, 2] == 45.0: atgb_data.append( [data[j, 0], data[j, 1], data[j, 2], data[j, 3], data[j, 10], data[j, 11], data[j, 12], data[j, 13], data[j, 22], data[j, 23], data[j, 24], data[j, 25]]) max_inc_onfile = atgb_data[-1][2] # Extend the range from current to 90 degrees (for [001] and [111] gbs) for j in range(len(atgb_data)): if axis[0] == 0 and axis[1] == 0 and axis[2] == 1: if 45 + atgb_data[j][2] > max_inc_onfile and 45 + atgb_data[j][2] <= 90: atgb_data.append( [atgb_data[j][0], atgb_data[j][1], 45 + atgb_data[j][2], atgb_data[j][3], data[j][10], data[j][11], data[j][12], data[j][13], data[j][22], data[j, 23], data[j, 24], data[j, 25]]) if axis[0] == 1 and axis[1] == 1 and axis[2] == 1: if 60 + atgb_data[j][2] > max_inc_onfile and 60 + atgb_data[j][2] <= 90: atgb_data.append( [atgb_data[j][0], atgb_data[j][1], 60 + atgb_data[j][2], atgb_data[j][3], data[j][10], data[j][11], data[j][12], data[j][13], data[j][22], data[j, 23], data[j, 24], data[j, 25]]) # Sort the atgb data w.r.t inclination a = np.array(atgb_data) col = 2 a = a[np.argsort(a[:, col])] return a
[docs] def gb_props(self,oilab_output_file = "/data/fcc0-10.txt",choose_decision=1): """ Assigns GB properties required for the calculation (read from oILAB output) and optionally prompt user to choose a disconnection mode. Args: oilab_output_file (str, optional): Path to the OILAB output file. Defaults to "/data/fcc0-10.txt". choose_decision (bool, optional): If True, allows user to choose disconnection mode. Defaults to True. Returns: tuple: - gb_data (np.ndarray): GB data including sigma, misorientation, inclination, etc. - burgers_vector (float): Burgers vector magnitude of selected disconnection mode. - step_height (float): Step height of the selected disconnection mode. """ lat_par = self.lattice_parameter period_cutoff = 10 atgb_data = self._find_ATGB_data(oilab_output_file,period_cutoff) gb_data = atgb_data[0, :] # GB properties p = atgb_data[0, 3] * lat_par b = atgb_data[0, 4] * lat_par h = atgb_data[0, 7] * lat_par H = atgb_data[0, 8] * lat_par print("+--------------------------------------------------------------------+") print("+ GB Kinetics +") print("+ This program generates atomistic images for motion of GB mediated +") print("+ by disconnection nucleation and glide +") print("+--------------------------------------------------------------------+") print("\n======================== GB information ==============================") print("Sigma = " + str(gb_data[0])) print("Misorientation = " + str(gb_data[1])) print("Inclination = " + str(gb_data[2])) print("Period = " + str(p)) print("Smallest burgers vector (b) = " + str(b)) print("Fundamental glide step height (hg) = " + str(h)) print("Distance between CSL planes (H) = " + str(H)) if choose_decision == False: m = 1 n = 0 else: print("\n================== Choose disconnection mode =========================") print("There exist infinitely many disconnection modes with the form:") print(" (b,h) = (m * b, m * hg + n * H) ") print("where m and n are integers \n") print("Example of a few of the possible disconnection modes are:") headers = ["m", "n", "|b|", "h", "coupling factor"] table_contents = [] for i in range(-3, 3, 1): for j in range(-3, 3, 1): if i == 0 and j == 0: continue br = i * b sh = i * h + j * H beta = br / sh table_contents.append([i, j, np.round(br, 2), np.round(sh, 2), np.round(beta, 2)]) for col in headers: print(col.ljust(10), end="") print() # Print table rows for i, row in enumerate(table_contents, start=1): for col in row: print(str(col).ljust(10), end="") print() m = int(input("\nEnter the m corresponding to the disconnection mode:")) n = int(input("Enter the n corresponding to the disconnection mode:")) burgers_vector = m * b step_height = m * h + n * H print("Disconnection mode considered in this run :") print("(b,h) = (" + str(np.round(burgers_vector, 2)) + "," + str(np.round(step_height, 2)) + "), coupling factor = " + str( np.round(burgers_vector / step_height, 2))) return gb_data, burgers_vector, step_height