def cent_rad_config(self, rmc6f_config): """ Method for figuring out the center and radius of the input RMC6F nano configuration. Provided nano RMC6F configuration, this method can extract information about the \ center and radius of the input nanoparticle configuration. Arguments: rmc6f_config {Object} -- Instance of `RMC6FReader` class Output: The center and radius of input nanoparticle configuration can be accessed \ from instance variable `centPos` and `NPRadius`. """ check_step = 0.02 start = timeit.default_timer() print("\nConfiguring particle center and radius...") x_temp = [p[0] for p in rmc6f_config.atomsCoord] y_temp = [p[1] for p in rmc6f_config.atomsCoord] z_temp = [p[2] for p in rmc6f_config.atomsCoord] self.centPos = [ sum(x_temp) / rmc6f_config.numAtoms, sum(y_temp) / rmc6f_config.numAtoms, sum(z_temp) / rmc6f_config.numAtoms ] self.centPosInt = [2 * x - 1.0 for x in self.centPos] none_beyond = False check_rad = check_step while not none_beyond: some_beyond = False for atom in rmc6f_config.atomsCoordInt: dist_temp = rmc6f_stuff.dist_calc_coord( self.centPosInt, atom, rmc6f_config.vectors) if dist_temp > check_rad: some_beyond = True break if some_beyond: check_rad += check_step else: none_beyond = True self.NPRadius = check_rad stop = timeit.default_timer() print("\n--------------------------------------------------") print("Particle center and radius successfully extracted.") print("Time taken:{0:11.3F} s".format(stop - start)) print("--------------------------------------------------") print("Particle radius = {0:10.2F} Angstrom".format(self.NPRadius)) print("--------------------------------------------------")
def cent_rad_config(self, rmc6f_config): check_step = 0.02 start = timeit.default_timer() print("\nConfiguring particle center and radius...") x_temp = [p[0] for p in rmc6f_config.atomsCoord] y_temp = [p[1] for p in rmc6f_config.atomsCoord] z_temp = [p[2] for p in rmc6f_config.atomsCoord] self.centPos = [sum(x_temp) / rmc6f_config.numAtoms, sum(y_temp) / rmc6f_config.numAtoms, sum(z_temp) / rmc6f_config.numAtoms] self.centPosInt = [2 * x - 1.0 for x in self.centPos] none_beyond = False check_rad = check_step while not none_beyond: some_beyond = False for atom in rmc6f_config.atomsCoordInt: dist_temp = rmc6f_stuff.dist_calc_coord(self.centPosInt, atom, rmc6f_config.vectors) if dist_temp > check_rad: some_beyond = True break if some_beyond: check_rad += check_step else: none_beyond = True self.NPRadius = check_rad stop = timeit.default_timer() print("\n--------------------------------------------------") print("Particle center and radius successfully extracted.") print("Time taken:{0:11.3F} s".format(stop - start)) print("--------------------------------------------------") print("Particle radius = {0:10.2F} Angstrom".format(self.NPRadius)) print("--------------------------------------------------")
def cent_r_rad_config(self, log_file, rmc6f_config): check_step = 0.02 start = timeit.default_timer() file_i = open(log_file, "r") for i in range(7): file_i.readline() line = file_i.readline() self.centPos = [float(x) for x in line.split()[2:]] self.centPosInt = [2 * x - 1.0 for x in self.centPos] none_beyond = False check_rad = check_step while not none_beyond: some_beyond = False for atom in rmc6f_config.atomsCoordInt: dist_temp = rmc6f_stuff.dist_calc_coord( self.centPosInt, atom, rmc6f_config.vectors) if dist_temp > check_rad: some_beyond = True break if some_beyond: check_rad += check_step else: none_beyond = True self.NPRadius = check_rad stop = timeit.default_timer() print("\n--------------------------------------------------") print("Particle center and radius successfully extracted.") print("Time taken:{0:11.3F} s".format(stop - start)) print("--------------------------------------------------") print("Particle radius = {0:10.2F} Angstrom".format(self.NPRadius)) print("--------------------------------------------------")
def bulk_to_shells(self, rmc6f_config): print("\n****************************************************") print("1->by thickness, 2->by number of atoms") print("****************************************************") div_scheme = int( input("Please select a way to divide particle into shells: ")) while div_scheme != 1 and div_scheme != 2: print("\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") print("!!!!'1' and '2' are only accepted inputs!!!!") print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") print("\n****************************************************") print("1->by thickness, 2->by number of atoms") print("****************************************************") div_scheme = int( input("Please select a way to divide particle into shells: ")) if div_scheme == 1: self.shellThickness = float( input("\nPlease input shell thickness for the analysis: ")) num_shells = int( input("\nPlease input number of shells to configure: ")) start = timeit.default_timer() print("\nDividing particle to various shells...") atoms_include = [i * 0 for i in range(rmc6f_config.numAtoms)] atoms_to_check = [i for i, x in enumerate(atoms_include) if x == 0] shell_atoms = [] print("\nShells to configure: ", end='', flush=True) for i in range(num_shells + 1): print("*", end='', flush=True) print("\nShells configured: ", end='', flush=True) for i in range(num_shells): shell_atoms.append([]) low_lim = i * self.shellThickness hi_lim = (i + 1) * self.shellThickness for ii in atoms_to_check: dist_temp = rmc6f_stuff.dist_calc_coord( self.centPosInt, rmc6f_config.atomsCoordInt[ii], rmc6f_config.vectors) if low_lim <= dist_temp < hi_lim: shell_atoms[i].append(ii) atoms_include[ii] = 1 atoms_to_check = [ i for i, x in enumerate(atoms_include) if x == 0 ] print(".", end='', flush=True) shell_atoms.append(atoms_to_check) num_shells += 1 print(".") stop = timeit.default_timer() print("\n\n--------------------------------------------") print("Particle successfully divided to " + str(num_shells) + " shells.") print("Time taken:{0:11.3F} s".format(stop - start)) print("--------------------------------------------") elif div_scheme == 2: num_shells = int( input("\nPlease input number of shells to configure: ")) print("\n*************************************************") for i in range(rmc6f_config.numTypeAtom): print(str(i + 1) + "->" + rmc6f_config.atomTypes[i] + " ", end='', flush=True) print("\n*************************************************") at_to_focus = int(input("Please input an atom type to focus on: ")) print("\n------------------------------------------") print("Total number of atoms selected: {0:10d}".format( rmc6f_config.numAtomEachType[at_to_focus - 1])) print("------------------------------------------") min_num_in_shell = int( input("Please input minimum number of " + rmc6f_config.atomTypes[at_to_focus - 1] + " atoms in shell: ")) start = timeit.default_timer() print("\nDividing particle to various shells...") print("\nShells to configure: ", end='', flush=True) for i in range(num_shells): print("*", end='', flush=True) atoms_include = [i * 0 for i in range(rmc6f_config.numAtoms)] atoms_to_check = [i for i, x in enumerate(atoms_include) if x == 0] low_lim = 0 hi_lim = 0 low_lim_out = [] hi_lim_out = [] check_step = 0.5 shell_atoms = [] shell_processed = 0 print("\nShells configured: ", end='', flush=True) while shell_processed < num_shells: shell_atoms.append([]) natoms_focus = 0 while natoms_focus < min_num_in_shell: hi_lim += check_step list_temp = [] burst = False for ii in atoms_to_check: dist_temp = rmc6f_stuff.dist_calc_coord( self.centPosInt, rmc6f_config.atomsCoordInt[ii], rmc6f_config.vectors) if low_lim <= dist_temp < hi_lim: list_temp.append(ii) space_left = min_num_in_shell - natoms_focus to_eat = 0 for item in list_temp: if rmc6f_config.atomsEle[ item] == rmc6f_config.atomTypes[at_to_focus - 1]: to_eat += 1 if space_left < int(0.1 * float(min_num_in_shell)) and \ to_eat > int(0.15 * float(min_num_in_shell)): burst = True if not burst: shell_atoms[shell_processed].extend(list_temp) for item in list_temp: atoms_include[item] = 1 if rmc6f_config.atomsEle[ item] == rmc6f_config.atomTypes[at_to_focus - 1]: natoms_focus += 1 else: hi_lim -= check_step break atoms_to_check = [ i for i, x in enumerate(atoms_include) if x == 0 ] low_lim_out.append(low_lim) hi_lim_out.append(hi_lim) shell_processed += 1 low_lim = hi_lim print(".", end='', flush=True) for i in range(num_shells): shell_atoms[i].sort() stop = timeit.default_timer() print("\n--------------------------------------------") print("Particle successfully divided to " + str(num_shells) + " shells.") print("Time taken:{0:11.3F} s".format(stop - start)) print("--------------------------------------------") dir_exist = True i = 1 while dir_exist: dir_check = rmc6f_config.fileName.split( ".")[0] + "_np_shells_" + str(i) if not os.path.exists(dir_check): dir_exist = False dir_use = dir_check i += 1 os.mkdir(dir_use) for i in range(num_shells): atoms_ele = [] atoms_line = [] line_num = 0 for item in shell_atoms[i]: line_num += 1 atoms_ele.append(rmc6f_config.atomsEle[item]) line_temp = rmc6f_config.atomsLine[item] line_new = str(line_num) + " " + " ".join( line_temp.split()[1:]) + "\n" atoms_line.append(line_new) atoms_ele_uniq = sorted(set(atoms_ele), key=atoms_ele.index) num_each_type = [] for j in range(len(atoms_ele_uniq)): num_each_type.append(str(atoms_ele.count(atoms_ele_uniq[j]))) num_rho_new = rmc6f_config.initNumRho * \ float(len(shell_atoms[i])) / \ float(rmc6f_config.numAtoms) header = rmc6f_config.header.copy() for j in range(len(header)): if "Number of atoms:" in header[j]: header[j] = "Number of atoms: " + str(len( shell_atoms[i])) + "\n" if "Atom types present:" in header[j]: header[j] = "Atom types present: " + " ".join( atoms_ele_uniq) + "\n" if "Number of types of atoms:" in header[j]: header[j] = "Number of types of atoms: " + str( len(atoms_ele_uniq)) + "\n" if "Number of each atom type:" in header[j]: header[j] = "Number of each atom type: " + " ".join( num_each_type) + "\n" if "Number density (Ang^-3):" in header[j]: header[j] = "Number density (Ang^-3):" + " {0:.6f}".format( num_rho_new) + "\n" file_out = open( os.path.join(dir_use, "shell_" + str(i)) + ".rmc6f", "w") for item in header: file_out.write(item) for item in atoms_line: file_out.write(item) file_out.close() now = datetime.datetime.now() log_file = open(os.path.join(dir_use, "np_shell_gen.log"), "w") log_file.write("==================================\n") log_file.write("Log file for np_to_shells routine.\n") log_file.write("==================================\n") log_file.write("Time stamp: " + str(now)[:19] + "\n") if div_scheme == 1: log_file.write( "================================================\n") log_file.write( "Particle divided into shells by equal thickness.\n") log_file.write("Shell thickness used: {0:10.1F}\n".format( self.shellThickness)) log_file.write( "Particle center: {0:10.6f}{1:10.6f}{2:10.6f}\n".format( self.centPos[0], self.centPos[1], self.centPos[2])) log_file.write( "Number of shells generated: {0:10d}\n".format(num_shells)) log_file.write("================================================") else: log_file.write( "================================================================\n" ) log_file.write( "Particle divided into shells by equal (roughly) number of atoms.\n" ) log_file.write("Atom type focused: {0:2s}\n".format( rmc6f_config.atomTypes[at_to_focus - 1])) log_file.write( "Particle center: {0:10.6f}{1:10.6f}{2:10.6f}\n".format( self.centPos[0], self.centPos[1], self.centPos[2])) log_file.write( "Minimum number of {0:2s} atom in each shell: {1:10d}\n". format(rmc6f_config.atomTypes[at_to_focus - 1], min_num_in_shell)) log_file.write( "Number of shells generated: {0:10d}\n".format(num_shells)) log_file.write( "================================================================\n" ) log_file.write("Lower and upper limit of each shell:\n") log_file.write( "================================================================\n" ) log_file.write("{0:>10s}{1:>10s}\n".format("R_min", "R_max")) for i in range(num_shells): log_file.write("{0:10.2F}{1:10.2F}\n".format( low_lim_out[i], hi_lim_out[i])) log_file.write( "================================================================" ) log_file.close() print("\n--------------------------------------------") print("RMC6F configs of shells output to directory:") print("--------------------------------------------") print(dir_use) print("--------------------------------------------")
def main(file_name): """ perco_net_old_config.py Usage: python perco_net_old_config.py RMC6F_FILE_NAME - Typing 'python perco_net_old_config.py -h' will print out - current help. Documentation: This script is written based on the algorithm presented in the following citation: J. Hoshen and R. Kopclman, Phys. Rev. B, 14 (1976), 3438-3445. Output: All Li atoms involved in the largest cluster will be saved into a separate RMC6F file. ATTENTION: THE HEADER IN THE OUTPUT RMC6F FILE WAS NOT CONFIGURED AUTOMATICALLY. THEREFORE, USER HAS TO MANUALLY CHANGE THE HEADER!!! Author: Yuanpeng Zhang @ Sun 6-Sep-20 Computational Instrument Scientist (CIS) @ ORNL """ input_config = rmc6f_stuff.RMC6FReader(file_name) # Read in user inputs - initial RMC6F file and percolation scheme. init_rmc6f_name = input("Please input initial RMC6F file name: ") print("=====================") print("0 -> 0TM only") print("1 -> 0TM & 1TM") print("2 -> 0TM, 1TM and 2TM") print("=====================") perco_scheme = -1 while perco_scheme != 0 or perco_scheme != 1 or perco_scheme != 2: perco_scheme = int(input("Please input percolation scheme: ")) # Read in the initial RMC6F file - for the purpose of initializing # the neighbor list, purely based on distances between atoms. init_config = rmc6f_stuff.RMC6FReader(init_rmc6f_name) init_atomsCoord = init_config.atomsCoordInt init_vectors = init_config.vectors # Figure out neighbor list, based on the provided initial RMC6F # configuration. neigh = {} for key, item in init_config.site_info_dict.items(): if item[1] != "O" and item[1] != "F": neigh[key] = [] for key1, item1 in init_config.site_info_dict.items(): if item1[1] != "O" and item1[1] != "F": # The 'dist_calc' function defined in current # script was there for historic reason. Later on, # I realized we have another more useful distance # calculator defined in 'rmc6f_stuff' module (which # is used below). Here I am lazy, so we still use # the one defined in current script. dist_temp = dist_calc(item[0], item1[0], init_vectors, init_atomsCoord) if 2.90 <= dist_temp <= 2.95: neigh[key].append(key1) # Figure out the gate for each neighbour. Focusing on one centering # atom, we have a few neighbors. Each neighbor has its own neighbor # list. We found that the gate for the centering atom and any of its # neighbor is just the common members of the neighbor list for both. # Among those common members (i. e. the final list of gate atoms for # each neighbor), we want to put those next to each other together, # for the purpose of further analysis for Li diffusion. gate = {} for key, item in neigh.items(): gate[key] = [] for label in item: neigh_temp = neigh[label] common = list(set(item) & set(neigh_temp)) common_sort = [common[0]] coord1 = init_config.site_info_dict[common[0]][2] for i in range(3): coord2 = init_config.site_info_dict[common[i + 1]][2] dist_temp = rmc6f_stuff.dist_calc_coord( coord1, coord2, init_vectors) if 2.90 <= dist_temp <= 2.95: common_sort.append(common[i + 1]) near = i + 1 for i in range(4): if i != 0 and i != near: common_sort.append(common[i]) gate[key].append(common_sort) cluster_num = 0 num_in_cluster = {} num_in_cluster[0] = 0 site_cluster = {} prop_cluster = {} # Initialize site cluster info. for i in range(input_config.scDim[2]): for j in range(input_config.scDim[1]): for k in range(input_config.scDim[0]): for ref_num in [1, 3, 5, 7]: label_temp = str(k) + "-" label_temp += (str(j) + "-") label_temp += (str(i) + "-") label_temp += str(ref_num) site_cluster[label_temp] = 0 for i in range(input_config.scDim[2]): for j in range(input_config.scDim[1]): for k in range(input_config.scDim[0]): for ref_num in [1, 3, 5, 7]: label_temp = str(k) + "-" label_temp += (str(j) + "-") label_temp += (str(i) + "-") label_temp += str(ref_num) # Here for each centering Li atom, we want to figure out # its real neighbors, i. e. those neighboring sites # occupied actually by Li, but not TM's. if input_config.site_info_dict[label_temp][1] == "Li": neigh_check = [] for i in range(len(neigh[label_temp])): nb_temp = neigh[label_temp][i] ele_temp = input_config.site_info_dict[nb_temp][1] if ele_temp == "Li": gate_ele = [] for item in gate[label_temp][i]: g_ele_temp = \ input_config.site_info_dict[item] gate_ele.append(g_ele_temp) if perco_scheme == 0: sub_cond1 = (gate_ele[0] == "Li") sub_cond2 = (gate_ele[1] == "Li") condition_1 = (sub_cond1 and sub_cond2) sub_cond1 = (gate_ele[2] == "Li") sub_cond2 = (gate_ele[3] == "Li") condition_2 = (sub_cond1 and sub_cond2) if condition_1 or condition_2: neigh_check.append( neigh[label_temp][i]) elif perco_scheme == 1: if "Li" in gate_ele: neigh_check.append( neigh[label_temp][i]) else: neigh_check.append(neigh[label_temp][i]) # Now we figured out the real neighbors, and the next # step is just to follow Fig. 1 in J. Hoshen's paper # (see the doc of current script). Here # 'neigh_check_temp' list contains those neighboring # Li sites which are already scanned (thus should be # with a non-zero cluster number). Li_neigh_found = False neigh_check_temp = [] for neigh_temp in neigh_check: if site_cluster[neigh_temp] != 0: neigh_check_temp.append(neigh_temp) Li_neigh_found = True # Here is the algorithm presented in Fig. 2 # in J. Hoshen's (see the doc of current # script). r = site_cluster[neigh_temp] t = r t = -num_in_cluster[t] if t < 0: prop_cluster[neigh_temp] = r else: r = t t = -num_in_cluster[t] if t < 0: prop_cluster[neigh_temp] = r else: while t > 0: r = t t = -num_in_cluster[t] cluster_temp = site_cluster[neigh_temp] num_in_cluster[cluster_temp] = -r prop_cluster[neigh_temp] = r if not Li_neigh_found: cluster_num += 1 site_cluster[label_temp] = cluster_num num_in_cluster[cluster_num] = 1 else: min_proper = 1E10 for item in neigh_check_temp: if prop_cluster[item] < min_proper: min_proper = prop_cluster[item] site_cluster[label_temp] = min_proper prop_c_uniq = [] for item in neigh_check_temp: if prop_cluster[item] not in prop_c_uniq: prop_c_uniq.append(prop_cluster[item]) temp_temp = 0 for item in prop_c_uniq: temp_temp += num_in_cluster[item] for item in prop_c_uniq: if item == min_proper: num_in_cluster[item] = temp_temp + 1 else: num_in_cluster[item] = -min_proper # Now, proceed to output stage. First, we figure out the proper # cluster for each site, based on their originally assigned # cluster number. Accoding to J. Hoshen, to figure out whether # a cluster number is a proper one or not is very simple - just # look at the number of atoms in the cluster. If it is positive, # then it is a proper cluster. Otherwise it is considered to be # connected to another proper cluster - the negative number then # specifies the connection (the corresponding positive number is # just the proper cluster that it is connected to). site_prop = {} for i in range(input_config.scDim[2]): for j in range(input_config.scDim[1]): for k in range(input_config.scDim[0]): for ref_num in [1, 3, 5, 7]: label_temp = str(k) + "-" label_temp += (str(j) + "-") label_temp += (str(i) + "-") label_temp += str(ref_num) num_temp = num_in_cluster[site_cluster[label_temp]] if num_temp > 0: site_prop[label_temp] = site_cluster[label_temp] elif num_temp < 0: site_prop[label_temp] = -num_temp # Output the number of Li atoms contained in all clusters. num_of_li_atoms_in_c_f = open("Number_of_Li_Atoms_in_Cluster.out", "w") num_of_li_atoms_in_c_f.write("Cluster\t# of Li atoms\n") for i in range(cluster_num): if num_in_cluster[i] > 0: num_of_li_atoms_in_c_f.write("{0:5d}{1:10d}\n".format( i, num_in_cluster[i])) num_of_li_atoms_in_c_f.close() # Figure out which cluster contains the most Li atoms. According to # J. Hoshen, the 'num_in_cluster' here in current script already # contains the contribution from all connected clusters. max_num = -1E10 for i in range(cluster_num): if num_in_cluster[i] > max_num: max_num = num_in_cluster[i] max_cluster = i # Find out all Li atoms contained in the largest cluster. Li_in_max_cluster = [] for i in range(input_config.scDim[2]): for j in range(input_config.scDim[1]): for k in range(input_config.scDim[0]): for ref_num in [1, 3, 5, 7]: label_temp = str(k) + "-" label_temp += (str(j) + "-") label_temp += (str(i) + "-") label_temp += str(ref_num) if site_prop[label_temp] == max_cluster: to_append = input_config.site_info_dict[label_temp][3] Li_in_max_cluster.append(to_append) # Write out RMC6F file. cluster_out = open("perco_cluster.out", "w") for item in input_config.header(): cluster_out.write(item) index = 0 for item in Li_in_max_cluster: index += 1 line_temp = str(index) + " " + " ".join(item.split()[1:]) cluster_out.write(line_temp) cluster_out.close()
def rms_strain_calc(rmc6f_config): start = timeit.default_timer() print("\nCalculating micro strain...") print("\nFirst, figuring out unique cells existing...") # Figure out unique cell list. cells = [] cell_num = 0 for i in range(rmc6f_config.numAtoms): cell_temp = [int(x) for x in rmc6f_config.atomsLine[i].split()[7:]] ref_temp = int(rmc6f_config.atomsLine[i].split()[6]) atom_i = i if i == 0: cell_exist = False else: cell_exist = False index = 0 for item in cells: if cell_temp[0] == item[0][0] and \ cell_temp[1] == item[0][1] and \ cell_temp[2] == item[0][2]: cell_exist = True break index += 1 if not cell_exist: cells.append([cell_temp, {}]) cells[cell_num][1][ref_temp] = atom_i cell_num += 1 else: cells[index][1][ref_temp] = atom_i # Store cell string for later search purpose. For example, cell '1 2 3' will # be stored as '123'. cells_string = [] for item in cells: str_temp = str(item[0][0]) + "," + str(item[0][1]) + "," + str(item[0][2]) cells_string.append(str_temp) # Configure number of atoms in one unit cell, assuming that we do have # at least one full unit cell. max_ref_num = 0 for item in cells: if len(item[1]) > max_ref_num: max_ref_num = len(item[1]) for i in range(len(cells)): if len(cells[i][1]) < max_ref_num: cells[i].append([0]) else: cells[i].append([1]) print("Unique cells successfully configured.") print("\nNow, figuring out neighbours of cells...") # Figure out neighbouring cells. for i in range(len(cells)): cells[i].append([]) if cells[i][0][0] == 0: a_min_1 = rmc6f_config.scDim[0] - 1 else: a_min_1 = cells[i][0][0] - 1 str_temp = str(a_min_1) + "," + str(cells[i][0][1]) + "," + str(cells[i][0][2]) if str_temp in cells_string: cells[i][3].append(cells_string.index(str_temp)) if cells[i][0][0] == rmc6f_config.scDim[0] - 1: a_plus_1 = 0 else: a_plus_1 = cells[i][0][0] + 1 str_temp = str(a_plus_1) + "," + str(cells[i][0][1]) + "," + str(cells[i][0][2]) if str_temp in cells_string: cells[i][3].append(cells_string.index(str_temp)) if cells[i][0][1] == 0: b_min_1 = rmc6f_config.scDim[1] - 1 else: b_min_1 = cells[i][0][1] - 1 str_temp = str(cells[i][0][0]) + "," + str(b_min_1) + "," + str(cells[i][0][2]) if str_temp in cells_string: cells[i][3].append(cells_string.index(str_temp)) if cells[i][0][1] == rmc6f_config.scDim[1] - 1: b_plus_1 = 0 else: b_plus_1 = cells[i][0][1] + 1 str_temp = str(cells[i][0][0]) + "," + str(b_plus_1) + "," + str(cells[i][0][2]) if str_temp in cells_string: cells[i][3].append(cells_string.index(str_temp)) if cells[i][0][2] == 0: c_min_1 = rmc6f_config.scDim[2] - 1 else: c_min_1 = cells[i][0][2] - 1 str_temp = str(cells[i][0][0]) + "," + str(cells[i][0][1]) + "," + str(c_min_1) if str_temp in cells_string: cells[i][3].append(cells_string.index(str_temp)) if cells[i][0][2] == rmc6f_config.scDim[2] - 1: c_plus_1 = 0 else: c_plus_1 = cells[i][0][2] + 1 str_temp = str(cells[i][0][0]) + "," + str(cells[i][0][1]) + "," + str(c_plus_1) if str_temp in cells_string: cells[i][3].append(cells_string.index(str_temp)) print("Neighbours of cells successfully configured.") print("\nChecking completeness of cells...") # Checking whether a cell should be included in calculating the unit cell # parameter. There are three criteria here: # 1. The cell should be full, i.e. no atoms missing. # 2. All neighbours should be present, namely, up, down, left, right, forward and backward. # 3. The neighbouring cells should also be full. for i in range(len(cells)): cell_full = (cells[i][2][0] == 1) cell_neigh_full = (len(cells[i][3]) == 6) cell_neighs_full = True for item in cells[i][3]: if cells[item][2][0] == 0: cell_neighs_full = False break if cell_full and cell_neigh_full and cell_neighs_full: cells[i].append([1]) else: cells[i].append([0]) print("Cells completeness successfully configured.") print("\nCalculating cell parameters...") # Calculating the cell parameter. a_list = [] valid_cell_num = 0 for item in cells: if item[4][0] == 1: a_temp = 0.0 valid_cell_num += 1 for k in item[1]: atom_1 = item[1][k] for i in range(6): atom_2 = cells[item[3][i]][1][k] if i < 2: vec_t = abs(rmc6f_config.atomsCoord[atom_1][0] - rmc6f_config.atomsCoord[atom_2][0]) elif i < 4: vec_t = abs(rmc6f_config.atomsCoord[atom_1][1] - rmc6f_config.atomsCoord[atom_2][1]) else: vec_t = abs(rmc6f_config.atomsCoord[atom_1][2] - rmc6f_config.atomsCoord[atom_2][2]) if vec_t > 0.5: vec_t = 1 - vec_t latt_a = np.asarray(rmc6f_config.vectors[0]) a_temp += (vec_t * la.norm(latt_a)) # if (vec_t * la.norm(latt_a) - 5.41046) > 1E-8: # print(item, k, i) a_list.append(a_temp/(float(len(item[1])) * 6.0)) if valid_cell_num == 0: print("\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") print("!!!!!!!!!!!No valid cells found!!!!!!!!!!!") print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") sys.exit() print("Lattice parameters of cells successfully calculated.") # Calculate the micro strain. a_bar = sum(a_list) / len(a_list) sum_t = 0 for item in a_list: sum_t += (item / a_bar - 1)**2 micro_strain = np.sqrt(sum_t / float(len(a_list))) ms_s_err = np.sqrt(2 * micro_strain**4 / (float(len(a_list) - 1))) file_exist = True i = 1 while file_exist: file_check = rmc6f_config.fileName.split(os.sep)[-1].split(".")[0] + "_" + str(i) + ".log" if not os.path.exists(file_check): file_exist = False file_use = file_check i += 1 now = datetime.datetime.now() log_file = open(file_use, "w") log_file.write("==================================\n") log_file.write("Log file for microstrain analysis.\n") log_file.write("==================================\n") log_file.write("Time stamp: " + str(now)[:19] + "\n") log_file.write("==================================\n") log_file.write("Total number of cells: {0:10d}\n".format(len(cells))) log_file.write("Number of valid cells: {0:10d}\n".format(valid_cell_num)) log_file.write("Microstrain: {0:8.6F} +/- {1:<8.6F}\n".format(micro_strain, ms_s_err)) log_file.write("==================================") log_file.close() stop = timeit.default_timer() print("\n------------------------------------------") print("Microstrain = {0:8.6F} +/- {1:<8.6F}".format(micro_strain, ms_s_err)) print("Time taken:{0:11.3F} s".format(stop - start)) print("------------------------------------------") print("Log information can be found here:") print("------------------------------------------") print(file_use) print("------------------------------------------") shell_analysis = input("\nDo you want to carry out shell analysis ([y]/n)?") if shell_analysis.upper() == "N": sys.exit() print("\nAnalyzing microstrain for shells...") for i in range(len(cells)): cells[i].append([0]) cell_shell = [] nano_particle = nano_stuff.NanoStuff() test_bulk = input("\nIs this for bulk ([n]/y)?") if test_bulk.upper() == "Y": nano_particle.centPosInt = [2.0 * random() - 1.0, 2.0 * random() - 1.0, 2.0 * random() - 1.0] print("\n-----------------------------------------------------") print("Center of the analysis sphere set to be (fractional):") print("{0:16.13F},{1:16.13F},{2:16.13F}".format((nano_particle.centPosInt[0] + 1.0) / 2.0, (nano_particle.centPosInt[1] + 1.0) / 2.0, (nano_particle.centPosInt[2] + 1.0) / 2.0)) print("-----------------------------------------------------") nano_particle.NPRadius = float(input("\nPlease input radius of the analysis sphere (in angstrom): ")) else: nano_particle.cent_rad_config(rmc6f_config) min_num_in_shell = int(input("\nPlease input minimum number of cells in shell: ")) start = timeit.default_timer() low_lim = 0 hi_lim = 0 low_lim_out = [] hi_lim_out = [] check_step = 0.5 shell_processed = 0 cells_configured = 0 enough_left = True while (hi_lim < nano_particle.NPRadius) and enough_left: cell_shell.append([]) cell_num = 0 while cell_num < min_num_in_shell: hi_lim += check_step list_temp = [] burst = False for ii in range(len(cells)): if cells[ii][4][0] == 1: dist_temp = rmc6f_stuff.dist_calc_coord(nano_particle.centPosInt, rmc6f_config.atomsCoordInt[cells[ii][1][1]], rmc6f_config.vectors) if (low_lim <= dist_temp < hi_lim) and cells[ii][5][0] == 0: list_temp.append(ii) space_left = min_num_in_shell - cell_num to_eat = len(list_temp) if space_left < int(0.1 * float(min_num_in_shell)) and \ to_eat > int(0.15 * float(min_num_in_shell)): burst = True if not burst: cell_shell[shell_processed].extend(list_temp) cells_configured += len(list_temp) for item in list_temp: cells[item][5][0] = 1 cell_num += len(list_temp) else: hi_lim -= check_step break low_lim_out.append(low_lim) hi_lim_out.append(hi_lim) shell_processed += 1 low_lim = hi_lim cells_left = len(a_list) - cells_configured if cells_left < 2 * min_num_in_shell: enough_left = False cell_shell.append([]) for i in range(len(cells)): if cells[i][4][0] == 1 and cells[i][5][0] == 0: cell_shell[shell_processed].append(i) hi_lim_out[shell_processed - 1] = nano_particle.NPRadius shell_ms = [] for shell in cell_shell: a_list = [] for item in shell: a_temp = 0.0 for k in cells[item][1]: atom_1 = cells[item][1][k] for i in range(6): atom_2 = cells[cells[item][3][i]][1][k] if i < 2: vec_t = abs(rmc6f_config.atomsCoord[atom_1][0] - rmc6f_config.atomsCoord[atom_2][0]) elif i < 4: vec_t = abs(rmc6f_config.atomsCoord[atom_1][1] - rmc6f_config.atomsCoord[atom_2][1]) else: vec_t = abs(rmc6f_config.atomsCoord[atom_1][2] - rmc6f_config.atomsCoord[atom_2][2]) if vec_t > 0.5: vec_t = 1 - vec_t latt_a = np.asarray(rmc6f_config.vectors[0]) a_temp += (vec_t * la.norm(latt_a)) a_list.append(a_temp / (float(len(cells[item][1])) * 6.0)) # Calculate the micro strain. a_bar = sum(a_list) / len(a_list) # Refer to the following discussion about the calculation # of variance of microstrain (which by itself is a variance). # In this case, we are calculating the variance of variance. sum_t = 0 for item in a_list: sum_t += (item / a_bar - 1) ** 2 micro_strain = np.sqrt(sum_t / float(len(a_list))) ms_s_err = np.sqrt(2 * micro_strain ** 4 / (float(len(a_list) - 1))) shell_ms.append([micro_strain, ms_s_err]) now = datetime.datetime.now() file_use = file_use.split(".")[0] + "_shells.log" log_file = open(file_use, "w") log_file.write("=====================================================\n") log_file.write("Log file for nanoparticle shell microstrain analysis.\n") log_file.write("=====================================================\n") log_file.write("Time stamp: " + str(now)[:19] + "\n") log_file.write("=====================================================\n") log_file.write("Analysis sphere center location (fractional): \n") log_file.write("{0:16.13F},{1:16.13F},{2:16.13F}\n".format((nano_particle.centPosInt[0] + 1.0) / 2.0, (nano_particle.centPosInt[1] + 1.0) / 2.0, (nano_particle.centPosInt[2] + 1.0) / 2.0)) log_file.write("=====================================================\n") log_file.write("Analysis sphere radius: {0:15.6F} angstrom.\n".format(nano_particle.NPRadius)) log_file.write("=====================================================\n") log_file.write("{0:>10s}{1:>12s}{2:>10s}{3:>10s}\n".format("Shell", "# of cells", "MS", "Err")) log_file.write("=====================================================\n") if test_bulk.upper() == "Y": for i in range(len(cell_shell) - 1): log_file.write("{0:10d}{1:12d}{2:10.6f}{3:10.6f}\n".format(i + 1, len(cell_shell[i]), shell_ms[i][0], shell_ms[i][1])) else: for i in range(len(cell_shell)): log_file.write("{0:10d}{1:12d}{2:10.6f}{3:10.6f}\n".format(i + 1, len(cell_shell[i]), shell_ms[i][0], shell_ms[i][1])) log_file.write("=====================================================") log_file.close() stop = timeit.default_timer() print("\n--------------------------------------------") print("Particle successfully divided to " + str(shell_processed) + " shells.") print("Time taken:{0:11.3F} s".format(stop - start)) print("--------------------------------------------") print("Log information can be found here:") print(file_use) print("--------------------------------------------")
def dgt_tensor(ref_config, rmc6f_config): r_cut = float(input("\nPlease input cutoff for neighbour analysis: ")) start = timeit.default_timer() print("\nFiguring out neighbours of atoms...") atoms_neigh = [] neigh_dist = [] for i in range(ref_config.numAtoms): atoms_neigh.append({}) neigh_dist.append([]) total = int((ref_config.numAtoms - 1) * ref_config.numAtoms / 2) processed = 0 print("Progress: ") for i in range(ref_config.numAtoms): for j in range(ref_config.numAtoms - (i + 1)): dist_temp = rmc6f_stuff.dist_calc_coord( ref_config.atomsCoordInt[i], ref_config.atomsCoordInt[i + 1 + j], ref_config.vectors) if dist_temp < r_cut: dist_in_neigh = False for item in neigh_dist[i]: if abs(dist_temp - item) < 1E-3: dist_in_neigh = True atoms_neigh[i][item].append(i + 1 + j) break if not dist_in_neigh: neigh_dist[i].append(dist_temp) atoms_neigh[i][dist_temp] = [i + 1 + j] dist_in_neigh = False for item in neigh_dist[i + 1 + j]: if abs(dist_temp - item) < 1E-3: dist_in_neigh = True atoms_neigh[i + 1 + j][item].append(i) break if not dist_in_neigh: neigh_dist[i + 1 + j].append(dist_temp) atoms_neigh[i + 1 + j][dist_temp] = [i] processed += 1 if processed % (int(total * 0.01)) == 0 and processed != total: if processed % (int(total * 0.01) * 5) == 0: print(str(ceil(processed * 100.0 / total)) + "%", end='', flush=True) else: print(".", end='', flush=True) if processed % (int(total * 0.01) * 20) == 0: print("") if (ceil(processed * 100.0 / total) == 100) and \ (processed % (int(total * 0.01) * 5) == 0): print("100%") stop = timeit.default_timer() print("\n--------------------------------------------") print("Neighbours of atoms successfully configured.") print("Time taken:{0:11.3F} s".format(stop - start)) print("--------------------------------------------") start = timeit.default_timer() print("\nCalculating the deformation gradient tensor...") total = sum(len(x) for x in atoms_neigh) processed = 0 print("Progress: ") dgt_out = [] for i in range(ref_config.numAtoms): d_mat = np.zeros([3, 3]) a_mat = np.zeros([3, 3]) for k in atoms_neigh[i]: for item in atoms_neigh[i][k]: vec_x_frac = [] for j in range(3): vec_temp = rmc6f_config.atomsCoord[item][ j] - rmc6f_config.atomsCoord[i][j] if vec_temp > 0.5: vec_temp -= 1.0 elif vec_temp < -0.5: vec_temp += 1.0 vec_x_frac.append(vec_temp) vec_xx_frac = [] for j in range(3): vec_temp = ref_config.atomsCoord[item][ j] - ref_config.atomsCoord[i][j] if vec_temp > 0.5: vec_temp -= 1.0 elif vec_temp < -0.5: vec_temp += 1.0 vec_xx_frac.append(vec_temp) vec_x_cart = [ sum(vec_x_frac[ii] * ref_config.vectors[ii][iii] for ii in range(3)) for iii in range(3) ] vec_xx_cart = [ sum(vec_xx_frac[ii] * ref_config.vectors[ii][iii] for ii in range(3)) for iii in range(3) ] r_temp = (k - min(neigh_dist[i])) / r_cut if r_temp <= 0.5: w_temp = 1.0 - 6.0 * r_temp**2 + 6.0 * r_temp**3 elif r_temp < 1.0: w_temp = 2.0 - 6.0 * r_temp + 6.0 * r_temp**2 - 2.0 * r_temp**3 else: w_temp = 0 d_temp = np.zeros([3, 3]) a_temp = np.zeros([3, 3]) for ii in range(3): for jj in range(3): d_temp[ii][ jj] = vec_xx_cart[ii] * vec_xx_cart[jj] * w_temp a_temp[ii][ jj] = vec_x_cart[ii] * vec_xx_cart[jj] * w_temp d_mat += d_temp a_mat += a_temp processed += 1 if processed % (int(total * 0.01)) == 0 and processed != total: if processed % (int(total * 0.01) * 5) == 0: print(str(ceil(processed * 100.0 / total)) + "%", end='', flush=True) else: print(".", end='', flush=True) if processed % (int(total * 0.01) * 20) == 0: print("") dgt_out.append(np.matmul(a_mat, inv(d_mat))) if (ceil(processed * 100.0 / total) == 100) and \ (processed % (int(total * 0.01) * 5) == 0): print("100%") epsilon_out = [] omega_out = [] for i in range(ref_config.numAtoms): epsilon_out.append(np.zeros([3, 3])) omega_out.append(np.zeros([3, 3])) epsilon_out[i][0][0] = dgt_out[i][0][0] epsilon_out[i][0][1] = (dgt_out[i][0][1] + dgt_out[i][1][0]) / 2.0 epsilon_out[i][0][2] = (dgt_out[i][0][2] + dgt_out[i][2][0]) / 2.0 epsilon_out[i][1][0] = epsilon_out[i][0][1] epsilon_out[i][1][1] = dgt_out[i][1][1] epsilon_out[i][1][2] = (dgt_out[i][1][2] + dgt_out[i][2][1]) / 2.0 epsilon_out[i][2][0] = epsilon_out[i][0][2] epsilon_out[i][2][1] = epsilon_out[i][1][2] epsilon_out[i][2][2] = dgt_out[i][2][2] omega_out[i][0][0] = 0 omega_out[i][0][1] = (dgt_out[i][0][1] - dgt_out[i][1][0]) / 2.0 omega_out[i][0][2] = (dgt_out[i][0][2] - dgt_out[i][2][0]) / 2.0 omega_out[i][1][0] = -omega_out[i][0][1] omega_out[i][1][1] = 0 omega_out[i][1][2] = (dgt_out[i][1][2] - dgt_out[i][2][1]) / 2.0 omega_out[i][2][0] = -omega_out[i][0][2] omega_out[i][2][1] = -omega_out[i][1][2] omega_out[i][2][2] = 0 strain_invar1 = [] strain_invar2 = [] for i in range(ref_config.numAtoms): strain_invar1.append(sum([epsilon_out[i][j][j] for j in range(3)])) sum_temp = 0 for j in range(3): for k in range(3): sum_temp += (epsilon_out[i][j][k] * epsilon_out[i][j][k] - epsilon_out[i][j][j] * epsilon_out[i][k][k]) strain_invar2.append(sum_temp / 2.0) base_name = os.path.basename(rmc6f_config.fileName) base_name = str(base_name.split(".rmc6f")[0]) dgt_out_file = open(base_name + "_dgt.out", "w") now = datetime.datetime.now() dgt_out_file.write( "=================================================================\n") dgt_out_file.write("Deformation gradient tensor for RMC6F config" + os.path.basename(rmc6f_config.fileName) + "\n") dgt_out_file.write( "=================================================================\n") dgt_out_file.write("Time stamp: " + str(now)[:19] + "\n") dgt_out_file.write( "=================================================================\n") dgt_out_file.write("Total number of atoms: " + str(rmc6f_config.numAtoms) + "\n") dgt_out_file.write( "=================================================================\n") dgt_out_file.write("{0:>10s}{1:>10s}{2:>10s}{3:>10s}{4:>10s}" "{5:>10s}{6:>10s}{7:>10s}{8:>10s}{9:>10s}\n".format( "Atoms", "e11", "e12", "e13", "e21", "e22", "e23", "e31", "e32", "e33")) for i in range(rmc6f_config.numAtoms): dgt_out_file.write("{0:>10d}{1:>10f}{2:>10f}{3:>10f}{4:>10f}" "{5:>10f}{6:>10f}{7:>10f}{8:>10f}{9:>10f}\n".format( i + 1, dgt_out[i][0][0], dgt_out[i][0][1], dgt_out[i][0][2], dgt_out[i][1][0], dgt_out[i][1][1], dgt_out[i][1][2], dgt_out[i][2][0], dgt_out[i][2][1], dgt_out[i][2][2])) dgt_out_file.write( "=================================================================") dgt_out_file.close() strain_out_file = open(base_name + "_strain.out", "w") now = datetime.datetime.now() strain_out_file.write( "=================================================================\n") strain_out_file.write("Strain tensor for RMC6F config" + os.path.basename(rmc6f_config.fileName) + "\n") strain_out_file.write( "=================================================================\n") strain_out_file.write("Time stamp: " + str(now)[:19] + "\n") strain_out_file.write( "=================================================================\n") strain_out_file.write("Total number of atoms: " + str(rmc6f_config.numAtoms) + "\n") strain_out_file.write( "=================================================================\n") strain_out_file.write("{0:>10s}{1:>10s}{2:>10s}{3:>10s}{4:>10s}" "{5:>10s}{6:>10s}{7:>10s}{8:>10s}{9:>10s}\n".format( "Atoms", "epsilon11", "epsilon12", "epsilon13", "epsilon21", "epsilon22", "epsilon23", "epsilon31", "epsilon32", "epsilon33")) for i in range(rmc6f_config.numAtoms): strain_out_file.write( "{0:>10d}{1:>10f}{2:>10f}{3:>10f}{4:>10f}" "{5:>10f}{6:>10f}{7:>10f}{8:>10f}{9:>10f}\n".format( i + 1, epsilon_out[i][0][0], epsilon_out[i][0][1], epsilon_out[i][0][2], epsilon_out[i][1][0], epsilon_out[i][1][1], epsilon_out[i][1][2], epsilon_out[i][2][0], epsilon_out[i][2][1], epsilon_out[i][2][2])) strain_out_file.write( "=================================================================") strain_out_file.close() rot_out_file = open(base_name + "_rot.out", "w") now = datetime.datetime.now() rot_out_file.write( "=================================================================\n") rot_out_file.write("Rotation tensor for RMC6F config" + os.path.basename(rmc6f_config.fileName) + "\n") rot_out_file.write( "=================================================================\n") rot_out_file.write("Time stamp: " + str(now)[:19] + "\n") rot_out_file.write( "=================================================================\n") rot_out_file.write("Total number of atoms: " + str(rmc6f_config.numAtoms) + "\n") rot_out_file.write( "=================================================================\n") rot_out_file.write("{0:>10s}{1:>10s}{2:>10s}{3:>10s}{4:>10s}" "{5:>10s}{6:>10s}{7:>10s}{8:>10s}{9:>10s}\n".format( "Atoms", "omega11", "omega12", "omega13", "omega21", "omega22", "omega23", "omega31", "omega32", "omega33")) for i in range(rmc6f_config.numAtoms): rot_out_file.write("{0:>10d}{1:>10f}{2:>10f}{3:>10f}{4:>10f}" "{5:>10f}{6:>10f}{7:>10f}{8:>10f}{9:>10f}\n".format( i + 1, omega_out[i][0][0], omega_out[i][0][1], omega_out[i][0][2], omega_out[i][1][0], omega_out[i][1][1], omega_out[i][1][2], omega_out[i][2][0], omega_out[i][2][1], omega_out[i][2][2])) rot_out_file.write( "=================================================================") rot_out_file.close() strain_invar_file = open(base_name + "_strain_invar.out", "w") now = datetime.datetime.now() strain_invar_file.write( "=================================================================\n") strain_invar_file.write("Strain invariant for RMC6F config" + os.path.basename(rmc6f_config.fileName) + "\n") strain_invar_file.write( "=================================================================\n") strain_invar_file.write("Time stamp: " + str(now)[:19] + "\n") strain_invar_file.write( "=================================================================\n") strain_invar_file.write("Total number of atoms: " + str(rmc6f_config.numAtoms) + "\n") strain_invar_file.write( "=================================================================\n") strain_invar_file.write("{0:>10s}{1:>10s}{2:>10s}\n".format( "Atoms", "Invar1", "Invar2")) for i in range(rmc6f_config.numAtoms): strain_invar_file.write("{0:>10d}{1:>10f}{2:>10f}\n".format( i + 1, strain_invar1[i], strain_invar2[i])) strain_invar_file.write( "=================================================================") strain_invar_file.close() stop = timeit.default_timer() print("\n----------------------------------------------------") print("Deformation gradient tensor successfully calculated.") print("Time taken:{0:11.3F} s".format(stop - start)) print("----------------------------------------------------") print("List of output files:") print("----------------------------------------------------") print("Deformation gradient tensor: " + base_name + "_dgt.out") print("Strain tensor: " + base_name + "_strain.out") print("Rotation tensor: " + base_name + "_rot.out") print("Strain invariants: " + base_name + "_strain_invar.out") print("----------------------------------------------------")