Exemple #1
0
    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("--------------------------------------------------")
Exemple #3
0
    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("----------------------------------------------------")