Esempio n. 1
0
    def get_trajectory(self):
        #
        outvars_after_computation_line = 0
        for i in range(len(self.lines)):
            if len(self.lines[i].split()) > 0 and self.lines[i].split(
            )[0] == "-outvars:" and self.lines[i].split()[5] == "after":
                outvars_after_computation_line = i
        #
        self.trajectory_final = []
        for i in range(outvars_after_computation_line, len(self.lines)):
            if len(self.lines[i].split()) > 0 and self.lines[i].split(
            )[0].split("_")[0] == "xangst":
                atm = []
                # doesn't know name now
                atm.append(
                    Atom("XXX", float(self.lines[i].split()[1]),
                         float(self.lines[i].split()[2]),
                         float(self.lines[i].split()[3])))
                j = i + 1
                while len(self.lines[j].split()) == 3:
                    atm.append(
                        Atom("XXX", float(self.lines[j].split()[0]),
                             float(self.lines[j].split()[1]),
                             float(self.lines[j].split()[2])))
                    j = j + 1
                self.trajectory_final.append(atm)

        #
        outvars_before_computation_line = 0
        for i in range(len(self.lines)):
            if len(self.lines[i].split()) > 0 and self.lines[i].split(
            )[0] == "-outvars:" and self.lines[i].split()[5] == "input":
                outvars_before_computation_line = i
        #
        self.trajectory_initial = []
        for i in range(outvars_before_computation_line, len(self.lines)):
            if len(self.lines[i].split()) > 0 and self.lines[i].split(
            )[0].split("_")[0] == "xangst":
                atm = []
                # doesn't know name now
                atm.append(
                    Atom("XXX", float(self.lines[i].split()[1]),
                         float(self.lines[i].split()[2]),
                         float(self.lines[i].split()[3])))
                j = i + 1
                while len(self.lines[j].split()) == 3:
                    atm.append(
                        Atom("XXX", float(self.lines[j].split()[0]),
                             float(self.lines[j].split()[1]),
                             float(self.lines[j].split()[2])))
                    j = j + 1
                self.trajectory_initial.append(atm)
Esempio n. 2
0
def enlarge_atoms_new_cell(structure, new_cell):
    """
    :return out:
        atoms: [
                ["C", 0.00000, 0.000000, 0.0000],
                ["O", 0.00000, 0.500000, 0.0000],
                ...
            ]
    Note: will enlarge the atoms in the unit cell along both a, b, c and -a, -b, -c direction of the new_cell !!!
        but the cell is not redefined, the returned atoms is not used to form crystal, but to be 
        tailored by redefine_lattice function to get atoms for the redfined lattice.
        The goal is to make sure when the new cell rotate in the 3D space, it will always be filled
        with atoms.    
    """
    from pymatflow.base.atom import Atom
    #
    cell = copy.deepcopy(structure.cell)
    a = np.linalg.norm(cell[0])
    b = np.linalg.norm(cell[1])
    c = np.linalg.norm(cell[2])

    new_a = np.linalg.norm(new_cell[0])
    new_b = np.linalg.norm(new_cell[1])
    new_c = np.linalg.norm(new_cell[2])

    n1 = np.ceil(
        np.max([new_a, new_b, new_c]) / a) * 2  # maybe times 2 is not needed
    n2 = np.ceil(np.max([new_a, new_b, new_c]) / b) * 2
    n3 = np.ceil(np.max([new_a, new_b, new_c]) / c) * 2
    n = [int(n1), int(n2), int(n3)]

    print("n1 n2 n3: %d %d %d\n" % (n1, n2, n3))
    atoms = copy.deepcopy(structure.atoms)
    print("atoms.size(): %d\n" % len(atoms))
    # build supercell: replica in three vector one by one
    for i in range(3):
        natom_now = len(atoms)
        for j in range(n[i] - 1):
            for atom in atoms[:natom_now]:
                x = atom.x + float(j + 1) * structure.cell[i][0]
                y = atom.y + float(j + 1) * structure.cell[i][1]
                z = atom.z + float(j + 1) * structure.cell[i][2]
                atoms.append(Atom(atom.name, x, y, z))
        # replicate in the negative direction of structure.cell[i]
        for atom in atoms[:natom_now * n[i]]:
            x = atom.x - float(n[i]) * structure.cell[i][0]
            y = atom.y - float(n[i]) * structure.cell[i][1]
            z = atom.z - float(n[i]) * structure.cell[i][2]
            atoms.append(Atom(atom.name, x, y, z))
    return [[atom.name, atom.x, atom.y, atom.z] for atom in atoms]
Esempio n. 3
0
def get_structure_from_url(url):
    from pymatflow.structure.crystal import crystal
    from pymatflow.base.atom import Atom

    result = _get_from_url(url)
    out = []
    for item in result["data"]:
        a = crystal()
        a.cell = item["unit_cell"]
        latcell = np.array(a.cell)
        convmat_frac_to_cartesian = latcell.T
        a.atoms = []
        for atm in item["sites"]:
            name = atm.split()[0]
            cartesian = list(
                convmat_frac_to_cartesian.dot(
                    np.array([
                        float(atm.split()[2]),
                        float(atm.split()[3]),
                        float(atm.split()[4])
                    ])))
            a.atoms.append(
                Atom(name=name, x=cartesian[0], y=cartesian[1],
                     z=cartesian[2]))
        out.append(a)
    return out
Esempio n. 4
0
 def build_supercell(self, n):
     """
     :param n: [n1, n2, n3]
     :return out:
         {
             "cell": [[], [], []],
             "atoms": [
                     ["C", 0.00000, 0.000000, 0.0000],
                     ["O", 0.00000, 0.500000, 0.0000],
                     ...
                 ]
         }
     Note: will not affect status of self
     """
     #
     cell = copy.deepcopy(self.cell)
     for i in range(3):
         for j in range(3):
             cell[i][j] = n[i] * self.cell[i][j]
     atoms = copy.deepcopy(self.atoms)
     # build supercell: replica in three vector one by one
     for i in range(3):
         natom_now = len(atoms)
         for j in range(n[i] - 1):
             for atom in atoms[:natom_now]:
                 x = atom.x + float(j + 1) * self.cell[i][0]
                 y = atom.y + float(j + 1) * self.cell[i][1]
                 z = atom.z + float(j + 1) * self.cell[i][2]
                 atoms.append(Atom(atom.name, x, y, z))
     return {
         "cell": cell,
         "atoms": [[atom.name, atom.x, atom.y, atom.z] for atom in atoms]
     }
Esempio n. 5
0
def interpolate(initial, final, nimage, moving_atom):
    """
    :param initial -> instance of pymatflow.structure.crystal.crystal()
    :param final -> instance of pymatflow.structure.crystal.crystal()
    :param nimage -> number of intermediate images(not including initial and final image)
    :param moving_atom -> the index of moving atoms (index starts from 0)
    :return images -> a list of instance of pymatflow.structure.crystal.crystal() 
            as inter images
    Feature:
        only interpolate(linearly) the specified moving atoms. other atoms may have
        different positions between initial and final image, however we use the coordinate
        of the initial image as the cooresponding coordinate for the inter image.
    """
    # use fractional coordinates to interpolate the images
    initial.natom = len(initial.atoms)
    final.natom = len(final.atoms)
    initial_frac = initial.get_fractional()
    final_frac = final.get_fractional()

    #images = []
    images_frac = []
    for i in range(nimage):
        #images.append(copy.deepcopy(initial))
        images_frac.append(copy.deepcopy(initial_frac))

    for i in moving_atom:
        dx = final_frac[i][1] - initial_frac[i][1]
        dy = final_frac[i][2] - initial_frac[i][2]
        dz = final_frac[i][3] - initial_frac[i][3]

        for j in range(nimage):
            x = initial_frac[i][1] + j * dx / (nimage + 1)
            y = initial_frac[i][2] + j * dy / (nimage + 1)
            z = initial_frac[i][3] + j * dz / (nimage + 1)
            #print("fractional x, y, z: %f %f %f\n" % (x, y, z))
            images_frac[j][i][1] = x
            images_frac[j][i][2] = y
            images_frac[j][i][3] = z
    # transform from images_frac to images
    # convert frac to cartesian again
    images = []
    latcell = np.array(initial.cell)
    convmat = latcell.T
    from pymatflow.base.atom import Atom
    from pymatflow.structure.crystal import crystal
    for i in range(nimage):
        img = crystal()
        img.atoms = []
        img.cell = initial.cell
        for atom in images_frac[i]:
            cartesian = list(convmat.dot(np.array([atom[1], atom[2],
                                                   atom[3]])))
            img.atoms.append(
                Atom(name=atom[0],
                     x=cartesian[0],
                     y=cartesian[1],
                     z=cartesian[2]))
        images.append(img)
        #
    return images
Esempio n. 6
0
def enlarge_atoms(structure):
    """
    :return out:
        atoms: [
                ["C", 0.00000, 0.000000, 0.0000],
                ["O", 0.00000, 0.500000, 0.0000],
                ...
            ]
    Note: will enlarge the atoms in the unit cell along both a, b, c and -a, -b, -c direction.
        The goal is to make sure when the cell rotate in the 3D space, it will always be filled
        with atoms.
    """
    from pymatflow.base.atom import Atom
    #
    cell = copy.deepcopy(structure.cell)
    a = np.linalg.norm(cell[0])
    b = np.linalg.norm(cell[1])
    c = np.linalg.norm(cell[2])

    n1 = np.ceil(np.max([a, b, c]) / a) * 2  # maybe times 2 is not needed
    n2 = np.ceil(np.max([a, b, c]) / b) * 2
    n3 = np.ceil(np.max([a, b, c]) / c) * 2
    n = [int(n1), int(n2), int(n3)]
    print(n)

    atoms = copy.deepcopy(structure.atoms)
    # build supercell: replica in three vector one by one
    for i in range(3):
        natom_now = len(atoms)
        for j in range(n[i] - 1):
            for atom in atoms[:natom_now]:
                x = atom.x + float(j + 1) * structure.cell[i][0]
                y = atom.y + float(j + 1) * structure.cell[i][1]
                z = atom.z + float(j + 1) * structure.cell[i][2]
                atoms.append(Atom(atom.name, x, y, z))
        # replicate in the negative direction of structure.cell[i]
        for atom in atoms[:natom_now * n[i]]:
            x = atom.x - float(n[i]) * structure.cell[i][0]
            y = atom.y - float(n[i]) * structure.cell[i][1]
            z = atom.z - float(n[i]) * structure.cell[i][2]
            atoms.append(Atom(atom.name, x, y, z))
    return [[atom.name, atom.x, atom.y, atom.z] for atom in atoms]
Esempio n. 7
0
def get_final_structure_from_aurl(aurl):
    """
    Note: get final structure(relaxed using vasp) from aurl, aurl is in format like this:
        aflowlib.duke.edu:AFLOWDATA/LIB3_RAW/Bi_dRh_pvTi_sv/T0003.ABC:LDAU2
        aflowlib.duke.edu:AFLOWDATA/ICSD_WEB/CUB/Cl1Na1_ICSD_622368
    """
    out = crystal()

    geometry = urlopen("http://" + aurl.replace(":AFLOWDATA", "/AFLOWDATA") +
                       "/?geometry").read().decode("utf-8")
    a, b, c, alpha, beta, gamma = [
        float(item) for item in geometry.replace("\n", "").split(",")
    ]
    alpha = alpha / 180 * np.pi
    beta = beta / 180 * np.pi
    gamma = gamma / 180 * np.pi

    out.cell = []
    out.cell.append([a, 0, 0])
    out.cell.append([np.cos(gamma) * b, np.sin(gamma) * b, 0])
    new_c1 = np.cos(beta)
    new_c2 = (np.cos(alpha - np.cos(beta) * np.cos(gamma))) / np.sin(gamma)
    new_c3_square = 1.0 - new_c1 * new_c1 - new_c2 * new_c2
    new_c3 = np.sqrt(new_c3_square)
    out.cell.append([new_c1 * c, new_c2 * c, new_c3 * c])

    elements_list = []
    composition = urlopen("http://" +
                          aurl.replace(":AFLOWDATA", "/AFLOWDATA") +
                          "/?composition").read().decode("utf-8")
    species = urlopen("http://" + aurl.replace(":AFLOWDATA", "/AFLOWDATA") +
                      "/?species").read().decode("utf-8")
    for i, number in enumerate(composition.replace("\n", "").split(",")):
        for j in range(int(number)):
            elements_list.append(species.replace("\n", "").split(",")[i])

    #
    convmat_frac_to_cartesian = np.array(out.cell).T
    out.atoms = []
    positions_fractional = urlopen(
        "http://" + aurl.replace(":AFLOWDATA", "/AFLOWDATA") +
        "/?positions_fractional").read().decode("utf-8")
    for i, item in enumerate(
            positions_fractional.replace("\n", "").split(";")):
        cartesian = convmat_frac_to_cartesian.dot(
            [float(k) for k in item.split(",")])
        out.atoms.append(
            Atom(name=elements_list[i],
                 x=cartesian[0],
                 y=cartesian[1],
                 z=cartesian[2]))
    #
    return out
Esempio n. 8
0
 def get_xyz(self, xyzfile):
     """
         get information to construct the structure from an xyz file
     """
     #self.file = xyzfile
     self.file = os.path.abspath(xyzfile)
     # now self.file is an absolute path, this is much easier for later usage
     with open(self.file, 'r') as fin:
         self.natom = int(fin.readline())
         fin.readline()
         i = 0
         while i < self.natom:
             line = fin.readline()
             atom = Atom(line.split()[0], float(line.split()[1]), float(line.split()[2]), float(line.split()[3]))
             # get information about the fix of this atom for opt and md
             if len(line.split()) == 4:
                 atom.fix = [False, False, False]
             elif line.split()[4][0] == '#':
                 atom.fix = [False, False, False] # the first char after coordinates is # , so cannpt set T or F
             else:
                 for j in range(3):
                     if line.split()[j+4] == 'T':
                         atom.fix[j] = True
                     elif line.split()[j+4] == 'F':
                         atom.fix[j] = False
                     else:
                         print("===============================\n")
                         print("warning: while read xyz file\n")
                         print("can only set T or F after coords\n")
                         sys.exit(1)
             # end get the information about the fix of this atom for opt and md
             self.atoms.append(atom)
             i += 1
     self.set_species_number()
     self.cell = self.get_cell(self.file)
Esempio n. 9
0
 def get_atoms(self, atoms):
     """
     :params cell: [[a1, a2, a3], [b1, b2, b3], [c1, c2, c3]] in unit of Anstrom
     :params atoms (in cartesian coordinates and unit of Anstrom)
             [
                 ["C", 0.00000, 0.0000000, 0.0000],
                 ["O", 1.12300, 3.3250000, 2.4893],
                 ....
             ]
     """
     self.atoms = [
         Atom(atoms[i][0], atoms[i][1], atoms[i][2], atoms[i][3])
         for i in range(len(atoms))
     ]
Esempio n. 10
0
def set_frac_min_to_zero(structure):
    """
    :return an object of crystal()
    Note:
        set the fractional coordinate minimum to zero, this is a way of standardize the cif file
    """
    from pymatflow.structure.crystal import crystal
    from pymatflow.base.atom import Atom

    # now calc the fractional coordinates
    atoms_frac = []
    latcell = np.array(structure.cell)
    convmat = np.linalg.inv(latcell.T)
    for i in range(len(structure.atoms)):
        atom = []
        atom.append(structure.atoms[i].name)
        atom = atom + list(
            convmat.dot(
                np.array([
                    structure.atoms[i].x, structure.atoms[i].y,
                    structure.atoms[i].z
                ])))
        atoms_frac.append(atom)

    # set the minimum of fractional coord to to 0
    min_frac_x = min(atoms_frac[:][1])
    min_frac_y = min(atoms_frac[:][2])
    min_frac_z = min(atoms_frac[:][3])
    for i in range(len(atoms_frac)):
        atoms_frac[i][1] -= min_frac_x
        atoms_frac[i][2] -= min_frac_y
        atoms_frac[i][3] -= min_frac_z

    # now convert coord of atom in atoms_frac_within_new_cell to cartesian
    out = crystal()
    out.atoms = []
    out.cell = structure.cell
    latcell = np.array(out.cell)
    convmat_frac_to_cartesian = latcell.T
    for atom in atoms_frac:
        cartesian = list(
            convmat_frac_to_cartesian.dot(np.array([atom[1], atom[2],
                                                    atom[3]])))
        out.atoms.append(
            Atom(name=atom[0], x=cartesian[0], y=cartesian[1], z=cartesian[2]))
    #

    return out
Esempio n. 11
0
def set_frac_within_zero_and_one(structure):
    """
    :return an object of crystal()
    Note:
        set the fractional coordinate within the range of 0 and 1, this is a way of standardize the cif file
    """
    from pymatflow.structure.crystal import crystal
    from pymatflow.base.atom import Atom

    # now calc the fractional coordinates
    atoms_frac = []
    latcell = np.array(structure.cell)
    convmat = np.linalg.inv(latcell.T)
    for i in range(len(structure.atoms)):
        atom = []
        atom.append(structure.atoms[i].name)
        atom = atom + list(
            convmat.dot(
                np.array([
                    structure.atoms[i].x, structure.atoms[i].y,
                    structure.atoms[i].z
                ])))
        atoms_frac.append(atom)

    # set the fractional coordinates within 0 and 1
    for i in range(len(atoms_frac)):
        for j in range(1, 4):
            while atoms_frac[i][j] >= 1:
                atoms_frac[i][j] -= 1
            while atoms_frac[i][j] < 0:
                atoms_frac[i][j] += 1

    # now convert coord of atom in atoms_frac_within_new_cell to cartesian
    out = crystal()
    out.atoms = []
    out.cell = structure.cell
    latcell = np.array(out.cell)
    convmat_frac_to_cartesian = latcell.T
    for atom in atoms_frac:
        cartesian = list(
            convmat_frac_to_cartesian.dot(np.array([atom[1], atom[2],
                                                    atom[3]])))
        out.atoms.append(
            Atom(name=atom[0], x=cartesian[0], y=cartesian[1], z=cartesian[2]))
    #
    return out
Esempio n. 12
0
def inverse_cell_center(structure):
    """
    make an inversion against the cell center
    :param structure: an instance of pymatflow.structure.crystal.crystal()
    """
    # first transfer to fractional coordinate and inverse against [0.5, 0.5, 0.5]
    structure.natom = len(structure.atoms)
    frac = structure.get_fractional()
    for atom in frac:
        atom[1] = 0.5 * 2 - atom[1]
        atom[2] = 0.5 * 2 - atom[2]
        atom[3] = 0.5 * 2 - atom[3]
    # convert frac to cartesian again
    latcell = np.array(structure.cell)
    convmat = latcell.T
    from pymatflow.base.atom import Atom
    structure.atoms = []
    for atom in frac:
        cartesian = list(convmat.dot(np.array([atom[1], atom[2], atom[3]])))
        structure.atoms.append(
            Atom(name=atom[0], x=cartesian[0], y=cartesian[1], z=cartesian[2]))
Esempio n. 13
0
    def get_scf_params_and_run_info(self):
        """
        self.run_info[]
            start_time: the task start time
            stop_time: the task stop time
            scf_energies: all the energies during the scf procedure
            #fermi_energy: fermi energy of the system (if output)

        """
        self.run_info["scf_energies"] = []

        for i in range(len(self.lines)):
            # if it is an empty line continue to next line
            if len(self.lines[i].split()) == 0:
                continue
            if self.lines[i].split()[0] == "Program" and self.lines[i].split()[1] == "PWSCF" and self.lines[i].split()[3] == "starts":
                self.run_info["start_time"] = self.lines[i].split("\n")[0]
            elif self.lines[i].split()[0] == "This" and self.lines[i].split()[1] == "run" and self.lines[i].split()[3] == "terminated":
                self.run_info["stop_time"] = self.lines[i].split("\n")[0]
            elif self.lines[i].split()[0] == "Parallel" and self.lines[i].split()[-1] == "processors":
                self.run_info["processors"] = int(self.lines[i].split()[-2])
            elif self.lines[i].split()[0] == "MPI" and self.lines[i].split()[-1] == "nodes":
                self.run_info["nodes"] = int(self.lines[i].split()[-2])
            elif self.lines[i].split()[0] == "bravais-lattice" and self.lines[i].split()[1] == "index":
                self.scf_params["alat_au"] = float(self.lines[i+1].split()[4])
                self.scf_params["nat"] = int(self.lines[i+3].split()[4])
                self.scf_params["nelectron"] = float(self.lines[i+5].split()[4])
                self.scf_params["n_ks_state"] = int(self.lines[i+6].split("=")[1])
                self.scf_params["ecutwfc"] = int(float(self.lines[i+7].split()[3]))
                self.scf_params["ecutrho"] = int(float(self.lines[i+8].split()[4]))
                self.scf_params["conv_thr"] = float(self.lines[i+9].split()[3])
                self.scf_params["mixing_beta"] = float(self.lines[i+10].split()[3])
            elif self.lines[i].split()[0] == "crystal" and self.lines[i].split()[1] == "axes:" and self.lines[i].split()[-1] =="alat)":
                self.scf_params["cell_a_alat"] = []
                self.scf_params["cell_a_alat"].append([float(self.lines[i+1].split()[3]), float(self.lines[i+1].split()[4]), float(self.lines[i+1].split()[5])])
                self.scf_params["cell_a_alat"].append([float(self.lines[i+2].split()[3]), float(self.lines[i+2].split()[4]), float(self.lines[i+2].split()[5])])
                self.scf_params["cell_a_alat"].append([float(self.lines[i+3].split()[3]), float(self.lines[i+3].split()[4]), float(self.lines[i+3].split()[5])])
            elif self.lines[i].split()[0] == "reciprocal" and self.lines[i].split()[1] == "axes:" and self.lines[i].split()[-1] == "pi/alat)": # actually '2 pi/alat'
                self.scf_params["cell_b_2pi_alat"] = []
                self.scf_params["cell_b_2pi_alat"].append([float(self.lines[i+1].split()[3]), float(self.lines[i+1].split()[4]), float(self.lines[i+1].split()[5])])
                self.scf_params["cell_b_2pi_alat"].append([float(self.lines[i+2].split()[3]), float(self.lines[i+2].split()[4]), float(self.lines[i+2].split()[5])])
                self.scf_params["cell_b_2pi_alat"].append([float(self.lines[i+3].split()[3]), float(self.lines[i+3].split()[4]), float(self.lines[i+3].split()[5])])
            elif self.lines[i].split()[0] == "site" and self.lines[i].split()[-1] == "units)" and self.lines[i].split()[-2] == "(alat":
                self.run_info["site_line_number"] = i
            elif self.lines[i].split()[0] == "number" and self.lines[i].split()[2] == 'k':
                if self.lines[i].split()[5] == "(tetrahedron":
                    self.scf_params["degauss"] = "tetrahedron method: degauss not needed"
                else:
                    self.scf_params["degauss"] = float(self.lines[i].split()[9])
                self.run_info["number-of-k-points"] = int(self.lines[i].split()[4])
            elif self.lines[i].split()[0] == "Estimated" and self.lines[i].split()[1] == "max":
                self.run_info["ram_per_process"] = self.lines[i].split()[7] + " " + self.lines[i].split()[8]
                self.run_info["total_ram"] = self.lines[i+2].split()[5] + " " + self.lines[i+2].split()[6]
            elif self.lines[i].split()[0] == "total" and self.lines[i].split()[1] == "energy":
                # the total energy of the last iteration is not print like the previous scf iteration
                # it begin with a ! total energy
                self.run_info["scf_energies"].append(float(self.lines[i].split()[3]))
            elif self.lines[i].split()[0] == "!" and self.lines[i].split()[5] == "Ry":
                self.run_info["scf_final_energy"] = float(self.lines[i].split()[4])
            elif self.lines[i].split()[0] == "convergence" and self.lines[i].split()[3] == "achieved":
                self.run_info["scf_iterations"] = int(self.lines[i].split()[5])
            elif self.lines[i].split()[0] ==  "the" and self.lines[i].split()[1] == "Fermi":
                self.run_info["fermi_energy"] = float(self.lines[i].split()[4])
            elif self.lines[i].split()[0] == "Total" and self.lines[i].split()[1] == "force":
                self.run_info["total_force"] = float(self.lines[i].split()[3])
            elif self.lines[i].split()[0] == "Computing" and self.lines[i].split()[-1] == "pressure":
                self.run_info["total_stress_ry_bohr_3"] = []
                self.run_info["total_stress_kbar"] = []
                self.run_info["pressure"] = float(self.lines[i+2].split()[-1])
                self.run_info["total_stress_ry_bohr_3"].append([float(self.lines[i+3].split()[0]), float(self.lines[i+3].split()[1]), float(self.lines[i+3].split()[2])])
                self.run_info["total_stress_ry_bohr_3"].append([float(self.lines[i+4].split()[0]), float(self.lines[i+4].split()[1]), float(self.lines[i+4].split()[2])])
                self.run_info["total_stress_ry_bohr_3"].append([float(self.lines[i+5].split()[0]), float(self.lines[i+5].split()[1]), float(self.lines[i+5].split()[2])])
                self.run_info["total_stress_kbar"].append([float(self.lines[i+3].split()[3]), float(self.lines[i+3].split()[4]), float(self.lines[i+3].split()[5])])
                self.run_info["total_stress_kbar"].append([float(self.lines[i+4].split()[3]), float(self.lines[i+4].split()[4]), float(self.lines[i+4].split()[5])])
                self.run_info["total_stress_kbar"].append([float(self.lines[i+5].split()[3]), float(self.lines[i+5].split()[4]), float(self.lines[i+5].split()[5])])

        # note: at present len(self.run_info["scf_energies"]) = len(self.run_info["scf_iterations"]) - 1
        # because the total energy of the last step is not printed in format like the previous scf step,
        # and it is printed as the '!    total energy              = ' where there is a "!" in the beginning
        # now we append the final scf step energy to self.run_info["scf_energies"]
        self.run_info["scf_energies"].append(self.run_info["scf_final_energy"])
        # ----------------------------------------------------------------------
        # get the xyz structure from information extracted above:
        self.xyz = base_xyz()
        self.xyz.natom = self.scf_params["nat"]
        begin = self.run_info["site_line_number"] + 1
        # Warning:
        # there are numeric erros when obtaining atom coordinated from qe output
        # in unit of alat and multiplied by alat and bohr. namely the acquired
        # atomic coordinates have numeric errors compared to the input xyz
        # so be cautious when use it.
        bohr = 0.529177208   # 1 Bohr = 0.529177208 Angstrom
        for i in range(self.xyz.natom):
            self.xyz.atoms.append(Atom(
                self.lines[begin+i].split()[1],
                self.scf_params["alat_au"] * bohr * float(self.lines[begin+i].split()[6]),
                self.scf_params["alat_au"] * bohr * float(self.lines[begin+i].split()[7]),
                self.scf_params["alat_au"] * bohr * float(self.lines[begin+i].split()[8])))
        self.xyz.cell = self.scf_params["cell_a_alat"] # now in unit of alat

        for i in range(3):
            for j in range(3):
                self.xyz.cell[i][j] = self.scf_params["cell_a_alat"][i][i] * self.scf_params["alat_au"] * bohr
Esempio n. 14
0
def redefine_lattice(structure, a, b, c, precision=1.0e-8):
    """
    :param a, b, c: new lattice vectors in terms of old.
        new_a = a[0] * old_a + a[1] * old_b + a[2] * old_c
        like a=[1, 0, 0], b=[0, 1, 0], c=[0, 0, 1] actually defines the
        same lattice as old.
    :param precision, a value that is less than 1 and infinitely close to 1
        used to judge whether one atom is in another periodic of the redefined cell
    :return an object of crystal()
    Method:
        first make a large enough supercell, which guarantee that all the atoms in the new lattice are inside
        the supercell.
        then redfine the cell, and calc the fractional coord of all atoms with regarding the new cell
        finally remove those atoms who's fractional coord is not within range [0, 1), and we can convert fractional
        coords to cartesian.
    Note:
        relationship among convertion of coords. the most important point is that all coords actually have one common
        reference system, namely the General XYZ coordinate system. all the cell are defined with XYZ as reference,
        and the convmat build from the cell(with XYZ as reference) can be applied only to atoms also with XYZ as ference,
        finally we convert frac to cartesian using convmat also defined using cell with XYZ as reference, so we get 
        the cartesian with general XYZ as reference. In the last all the coord of atoms and cell have the general 
        XYZ  system as reference. So it works!
    """
    from pymatflow.structure.crystal import crystal
    from pymatflow.base.atom import Atom
    old_cell = copy.deepcopy(structure.cell)
    new_cell = copy.deepcopy(structure.cell)
    new_cell[0] = list(a[0] * np.array(old_cell[0]) +
                       a[1] * np.array(old_cell[1]) +
                       a[2] * np.array(old_cell[2]))
    new_cell[1] = list(b[0] * np.array(old_cell[0]) +
                       b[1] * np.array(old_cell[1]) +
                       b[2] * np.array(old_cell[2]))
    new_cell[2] = list(c[0] * np.array(old_cell[0]) +
                       c[1] * np.array(old_cell[1]) +
                       c[2] * np.array(old_cell[2]))

    # enlarge the system
    atoms_container = crystal()
    atoms_container.get_atoms(
        enlarge_atoms_new_cell(structure=structure, new_cell=new_cell))

    print("atoms_container.size(): %d\n" % len(atoms_container.atoms))
    # now calc the fractional coordinates of all atoms in atoms_container with new_cell as reference
    atoms_frac = []
    latcell_new = np.array(new_cell)
    convmat_new = np.linalg.inv(latcell_new.T)
    for i in range(len(atoms_container.atoms)):
        atom = []
        atom.append(atoms_container.atoms[i].name)
        atom = atom + list(
            convmat_new.dot(
                np.array([
                    atoms_container.atoms[i].x, atoms_container.atoms[i].y,
                    atoms_container.atoms[i].z
                ])))
        atoms_frac.append(atom)

    atoms_frac_within_new_cell = []
    for atom in atoms_frac:
        if (0 <= atom[1] <
            (1 - precision)) and (0 <= atom[2] <
                                  (1 - precision)) and (0 <= atom[3] <
                                                        (1 - precision)):
            atoms_frac_within_new_cell.append(atom)

    # now convert coord of atom in atoms_frac_within_new_cell to cartesian
    out = crystal()
    out.atoms = []
    latcell_new = np.array(new_cell)
    convmat_frac_to_cartesian = latcell_new.T
    for atom in atoms_frac_within_new_cell:
        cartesian = list(
            convmat_frac_to_cartesian.dot(np.array([atom[1], atom[2],
                                                    atom[3]])))
        out.atoms.append(
            Atom(name=atom[0], x=cartesian[0], y=cartesian[1], z=cartesian[2]))
    #

    out.cell = new_cell

    return out
Esempio n. 15
0
    def get_opt_params_and_run_info(self):
        """
        self.run_info[]
            start_time: the task start time
            stop_time: the task stop time
            #scf_energies: all the energies during the scf procedure
            #fermi_energy: fermi energy of the system (if output)

        """
        #self.run_info["scf_energies"] = []

        for i in range(len(self.lines)):
            # if it is an empty line continue to next line
            if len(self.lines[i].split()) == 0:
                continue
            if self.lines[i].split()[0] == "Program" and self.lines[i].split(
            )[1] == "PWSCF" and self.lines[i].split()[3] == "starts":
                self.run_info["start_time"] = self.lines[i].split("\n")[0]
            elif self.lines[i].split()[0] == "This" and self.lines[i].split(
            )[1] == "run" and self.lines[i].split()[3] == "terminated":
                self.run_info["stop_time"] = self.lines[i].split("\n")[0]
            elif self.lines[i].split(
            )[0] == "Parallel" and self.lines[i].split()[-1] == "processors":
                self.run_info["processors"] = int(self.lines[i].split()[-2])
            elif self.lines[i].split()[0] == "MPI" and self.lines[i].split(
            )[-1] == "nodes":
                self.run_info["nodes"] = int(self.lines[i].split()[-2])
            elif self.lines[i].split(
            )[0] == "bravais-lattice" and self.lines[i].split()[1] == "index":
                self.opt_params["alat_au"] = float(self.lines[i +
                                                              1].split()[4])
                self.opt_params["nat"] = int(self.lines[i + 3].split()[4])
                self.opt_params["nelectron"] = float(self.lines[i +
                                                                5].split()[4])
                self.opt_params["n_ks_state"] = int(
                    self.lines[i + 6].split("=")[1])
                self.opt_params["ecutwfc"] = int(
                    float(self.lines[i + 7].split()[3]))
                self.opt_params["ecutrho"] = int(
                    float(self.lines[i + 8].split()[4]))
                self.opt_params["conv_thr"] = float(self.lines[i +
                                                               9].split()[3])
                self.opt_params["mixing_beta"] = float(
                    self.lines[i + 10].split()[3])
                if "nstep" in self.opt_params and self.opt_params[
                        "nstep"] != None:
                    pass
                else:
                    self.opt_params["nstep"] = int(self.lines[i +
                                                              13].split()[2])
            elif self.lines[i].split()[0] == "crystal" and self.lines[i].split(
            )[1] == "axes:" and self.lines[i].split()[-1] == "alat)":
                self.opt_params["cell_a_alat"] = []
                self.opt_params["cell_a_alat"].append([
                    float(self.lines[i + 1].split()[3]),
                    float(self.lines[i + 1].split()[4]),
                    float(self.lines[i + 1].split()[5])
                ])
                self.opt_params["cell_a_alat"].append([
                    float(self.lines[i + 2].split()[3]),
                    float(self.lines[i + 2].split()[4]),
                    float(self.lines[i + 2].split()[5])
                ])
                self.opt_params["cell_a_alat"].append([
                    float(self.lines[i + 3].split()[3]),
                    float(self.lines[i + 3].split()[4]),
                    float(self.lines[i + 3].split()[5])
                ])
            elif self.lines[i].split()[0] == "reciprocal" and self.lines[
                    i].split()[1] == "axes:" and self.lines[i].split(
                    )[-1] == "pi/alat)":  # actually '2 pi/alat'
                self.opt_params["cell_b_2pi_alat"] = []
                self.opt_params["cell_b_2pi_alat"].append([
                    float(self.lines[i + 1].split()[3]),
                    float(self.lines[i + 1].split()[4]),
                    float(self.lines[i + 1].split()[5])
                ])
                self.opt_params["cell_b_2pi_alat"].append([
                    float(self.lines[i + 2].split()[3]),
                    float(self.lines[i + 2].split()[4]),
                    float(self.lines[i + 2].split()[5])
                ])
                self.opt_params["cell_b_2pi_alat"].append([
                    float(self.lines[i + 3].split()[3]),
                    float(self.lines[i + 3].split()[4]),
                    float(self.lines[i + 3].split()[5])
                ])
            elif self.lines[i].split()[0] == "site" and self.lines[i].split(
            )[-1] == "units)" and self.lines[i].split()[-2] == "(alat":
                self.run_info["site_line_number"] = i
            elif self.lines[i].split()[0] == "number" and self.lines[i].split(
            )[2] == 'k':
                if self.lines[i].split()[5] == "(tetrahedron":
                    self.opt_params[
                        "degauss"] = "tetrahedron method: degauss not needed"
                else:
                    self.opt_params["degauss"] = float(
                        self.lines[i].split()[9])
                self.run_info["number-of-k-points"] = int(
                    self.lines[i].split()[4])
            elif self.lines[i].split(
            )[0] == "Estimated" and self.lines[i].split()[1] == "max":
                self.run_info["ram_per_process"] = self.lines[i].split(
                )[7] + " " + self.lines[i].split()[8]
                self.run_info["total_ram"] = self.lines[
                    i + 2].split()[5] + " " + self.lines[i + 2].split()[6]

        # ----------------------------------------------------------------------
        # get the input xyz structure from information extracted above:
        self.xyz = base_xyz()
        self.xyz.natom = self.opt_params["nat"]
        begin = self.run_info["site_line_number"] + 1
        # Warning:
        # there are numeric erros when obtaining atom coordinated from qe output
        # in unit of alat and multiplied by alat and bohr. namely the acquired
        # atomic coordinates have numeric errors compared to the input xyz
        # so be cautious when use it.
        bohr = 0.529177208  # 1 Bohr = 0.529177208 Angstrom
        for i in range(self.xyz.natom):
            self.xyz.atoms.append(
                Atom(
                    self.lines[begin + i].split()[1],
                    self.opt_params["alat_au"] * bohr *
                    float(self.lines[begin + i].split()[6]),
                    self.opt_params["alat_au"] * bohr *
                    float(self.lines[begin + i].split()[7]),
                    self.opt_params["alat_au"] * bohr *
                    float(self.lines[begin + i].split()[8])))
        self.xyz.cell = self.opt_params["cell_a_alat"]  # now in unit of alat

        for i in range(3):
            for j in range(3):
                self.xyz.cell[i][j] = self.opt_params["cell_a_alat"][i][
                    j] * self.opt_params["alat_au"] * bohr
        # now self.xyz.cell are in unit of Angstrom
        # ----------------------------------------------------------------------

        # get information output each ion step
        self._get_info_for_each_ions_step()
Esempio n. 16
0
    def get_trajectory(self):
        """
        Note: initial input structure is not in self.trajectory, but is in self.xyz
            self.trajectory contains all other structures and the last of it
            is the optimized structure.
        """
        if self.run_type == "relax":
            self.trajectory = []
            for i in range(len(self.lines)):
                if len(self.lines[i].split()) > 0 and self.lines[i].split(
                )[0] == "ATOMIC_POSITIONS":
                    if self.lines[i].split()[1] == "(angstrom)":
                        atm = []
                        j = i + 1
                        while len(self.lines[j].split()) == 4 or len(
                                self.lines[j].split()) == 7:
                            atm.append(
                                Atom(self.lines[j].split()[0],
                                     float(self.lines[j].split()[1]),
                                     float(self.lines[j].split()[2]),
                                     float(self.lines[j].split()[3])))
                            j = j + 1
                        self.trajectory.append(atm)
                    elif self.lines[i].split()[1] == "(crystal)":
                        # convert frac to cartesian again
                        latcell = np.array(self.xyz.cell)
                        convmat = latcell.T

                        atm = []
                        j = i + 1
                        while len(self.lines[j].split()) == 4 or len(
                                self.lines[j].split()) == 7:
                            x, y, z = list(
                                convmat.dot(
                                    np.array([
                                        float(self.lines[j].split()[1]),
                                        float(self.lines[j].split()[2]),
                                        float(self.lines[j].split()[3])
                                    ])))
                            atm.append(Atom(self.lines[j].split()[0], x, y, z))
                            j = j + 1
                        self.trajectory.append(atm)
            #
        elif self.run_type == "vc-relax":
            self.trajectory = []
            for i in range(len(self.lines)):
                if len(self.lines[i].split()) > 0 and self.lines[i].split(
                )[0] == "ATOMIC_POSITIONS":
                    if self.lines[i].split()[1] == "(angstrom)":
                        atm = []
                        j = i + 1
                        while len(self.lines[j].split()) == 4 or len(
                                self.lines[j].split()) == 7:
                            atm.append(
                                Atom(self.lines[j].split()[0],
                                     float(self.lines[j].split()[1]),
                                     float(self.lines[j].split()[2]),
                                     float(self.lines[j].split()[3])))
                            j = j + 1
                        self.trajectory.append(atm)
                    elif self.lines[i].split()[1] == "(crystal)":
                        cell = []
                        for k in range(3):
                            cell.append([
                                float(self.lines[i - 4 + k].split()[0]),
                                float(self.lines[i - 4 + k].split()[1]),
                                float(self.lines[i - 4 + k].split()[2])
                            ])
                        # convert frac to cartesian again
                        latcell = np.array(cell)
                        convmat = latcell.T

                        atm = []
                        j = i + 1
                        while len(self.lines[j].split()) == 4 or len(
                                self.lines[j].split()) == 7:
                            x, y, z = list(
                                convmat.dot(
                                    np.array([
                                        float(self.lines[j].split()[1]),
                                        float(self.lines[j].split()[2]),
                                        float(self.lines[j].split()[3])
                                    ])))
                            atm.append(Atom(self.lines[j].split()[0], x, y, z))
                            j = j + 1
                        self.trajectory.append(atm)
            #
        if self.relaxed == True and self.run_type == "vc-relax":
            # get the line number of the 'Begin final coordinates'
            # and 'End final coordinates'
            begin_final_coord_line = 0
            end_final_coord_line = 0
            while self.lines[
                    begin_final_coord_line] != "Begin final coordinates\n":
                begin_final_coord_line += 1
            while self.lines[end_final_coord_line] != "End final coordinates\n":
                end_final_coord_line += 1
            # get the optimized cell
            self.final_cell = []
            for i in range(begin_final_coord_line + 5,
                           begin_final_coord_line + 8):
                vec = []
                for j in range(3):
                    vec.append(float(self.lines[i].split()[j]))
                self.final_cell.append(vec)
Esempio n. 17
0
    def _get_trajectory(self):
        """
        1 Bohr=0.5291772108 Angstroms
        xcart is in unit of bohr
        so we convert it to angstrom
        Note:
        """
        bohr = 0.5291772108
        self.info["trajectory"] = []
        self.info["cells"] = []
        self.info["acells"] = []
        self.info["rprimds"] = []
        for i in range(len(self.lines)):
            if len(self.lines[i].split()) == 0:
                continue
            # used to get the trajectory
            if self.lines[i].split()[0] == "Cartesian" and self.lines[i].split(
            )[1] == "coordinates" and self.lines[i].split()[2] == "(xcart)":
                atm = []
                j = i + 1
                while self.lines[j].split()[1] != "coordinates":
                    atm.append(
                        Atom("XXX",
                             float(self.lines[j].split()[0]) * bohr,
                             float(self.lines[j].split()[1]) * bohr,
                             float(self.lines[j].split()[2]) * bohr))
                    j = j + 1
                self.info["trajectory"].append(atm)
            # --------------------------------
            # get the self.info["acells"] and self.info["rprimds"]
            # if not opt cell, the following two if code will never run to get the cell
            # so we can finally judget whether it is optcell by chekcing the length of self.info["rprimds"] and self.info["acells"]
            if self.lines[i].split()[0] == "Scale" and self.lines[i].split(
            )[4] == "(acell)":
                self.info["acells"].append([
                    float(self.lines[i + 1].split()[0]),
                    float(self.lines[i + 1].split()[1]),
                    float(self.lines[i + 1].split()[2])
                ])
            if self.lines[i].split()[0] == "Real" and self.lines[i].split(
            )[1] == "space" and self.lines[i].split()[4] == "(rprimd)":
                rprimd = []
                for k in range(3):
                    for j in range(3):
                        rprimd.append(float(self.lines[i + 1 + k].split()[j]))
                self.info["rprimds"].append(rprimd)
            # end extract the cell
            # --------------------------------

        if len(self.info["acells"]) == len(self.info["trajectory"]):
            # so cell is optimized. acell and rprimd is print every ion step
            # Note rprimd is not rprim!!! and rprimd is actually rprim already scaled by acell
            # so to build cell from rprimd, we only need to multiply by bohr, and don't need to scale by acell
            self.info["cells"] = copy.deepcopy(self.info["rprimds"])
            for m in range(len(self.info["cells"])):
                for n in range(9):
                    self.info["cells"][m][n] = self.info["cells"][m][n] * bohr
            #

        # now using self.atom_type from self.get_pseudo_info()
        for i in range(len(self.info["trajectory"])):
            for j in range(len(self.info["trajectory"][i])):
                name = self.atom_type[self.info["outvars"]["before"]["typat"]
                                      [j]]
                self.info["trajectory"][i][j].set_name(name)
Esempio n. 18
0
def main():
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(
        dest="driver",
        title="subcommands",
        description="choose one and only one subcommand")

    # --------------------------------------------------------------------------
    # supercell builder
    # --------------------------------------------------------------------------
    subparser = subparsers.add_parser("supercell",
                                      help="using supercell subcommand")

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           required=True,
                           help="input structure file")

    subparser.add_argument("-o",
                           "--output",
                           type=str,
                           required=True,
                           help="output structure file")

    subparser.add_argument("-n",
                           "--supern",
                           nargs="+",
                           type=int,
                           help="bulid supern:[int, int, int] supercell")

    # --------------------------------------------------------------------------
    # fix atoms
    # --------------------------------------------------------------------------
    subparser = subparsers.add_parser("fix", help="using fix subcommand")

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           required=True,
                           help="input structure file")

    subparser.add_argument("-o",
                           "--output",
                           type=str,
                           required=True,
                           help="output structure file")

    subparser.add_argument(
        "--fix",
        help=
        "list of fixed atoms, index start from 1, have privilege over --around-z",
        nargs='+',
        type=int,
        default=None)

    subparser.add_argument(
        "--around-z",
        type=float,
        nargs=3,
        default=None,
        help=
        "select atoms around specified z in Angstrom with tolerance, like this --around-z 10 -0.5 0.5"
    )

    subparser.add_argument(
        "--color",
        type=str,
        default="white",
        choices=["red", "green", "blue", "white"],
        help=
        "select color to color the fix atoms in xsd file, can be: red green blue and white"
    )

    # --------------------------------------------------------------------------
    # convert file type
    # --------------------------------------------------------------------------
    subparser = subparsers.add_parser("convert",
                                      help="using convert subcommand")

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           required=True,
                           help="input structure file")

    subparser.add_argument("-o",
                           "--output",
                           type=str,
                           required=True,
                           help="output structure file")

    # --------------------------------------------------------------------------
    # kpath
    # --------------------------------------------------------------------------
    subparser = subparsers.add_parser("kpath", help="using kpath subcommand")

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           required=True,
                           help="input structure file")

    subparser.add_argument("--engine",
                           type=str,
                           default="seekpath",
                           choices=["seekpath"],
                           help="choose tool to generate kpath")

    subparser.add_argument("--kpath-file",
                           type=str,
                           default="kpath-from-seekpath.txt",
                           help="the output kpoints file")

    # ---------------------------------------------------------------------------------
    # move atoms along one direction
    # ---------------------------------------------------------------------------------
    subparser = subparsers.add_parser("move",
                                      help="move atoms along one direction")

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           required=True,
                           help="input structure file")

    subparser.add_argument("-o",
                           "--output",
                           type=str,
                           required=True,
                           help="output structure file")

    subparser.add_argument("--atoms",
                           type=int,
                           nargs="+",
                           help="atoms to move, index start from 1")

    subparser.add_argument(
        "--direction",
        type=float,
        nargs=3,
        help=
        "direction to move the atoms, in format of crystal orientation index")

    subparser.add_argument(
        "--disp",
        type=float,
        help="displacement along the moving direction, in unit of Anstrom")

    # ---------------------------------------------------------------------------------
    # remove atoms
    # ---------------------------------------------------------------------------------
    subparser = subparsers.add_parser("remove", help="remove specified atoms")

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           required=True,
                           help="input structure file")

    subparser.add_argument("-o",
                           "--output",
                           type=str,
                           required=True,
                           help="output structure file")

    subparser.add_argument("--atoms",
                           type=int,
                           nargs="+",
                           help="atoms to remove, index start from 1")

    subparser.add_argument("--elements",
                           type=str,
                           nargs="+",
                           help="elements to remove")

    # ---------------------------------------------------------------------------------
    # vacuum layer
    # ---------------------------------------------------------------------------------
    subparser = subparsers.add_parser("vacuum", help="add vacuum layer")

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           required=True,
                           help="input structure file")

    subparser.add_argument("-o",
                           "--output",
                           type=str,
                           required=True,
                           help="output structure file")

    subparser.add_argument(
        "--plane",
        type=int,
        default=1,
        help="on which plane to add vacuum layer. 1: ab, 2: ac, 3: bc")

    subparser.add_argument(
        "--thick",
        type=float,
        default=10,
        help="thickness of the vacuum layer, in unit of Angstrom, default is 10"
    )

    # ---------------------------------------------------------------------------------
    # inverse atoms against geometric center
    # ---------------------------------------------------------------------------------
    subparser = subparsers.add_parser("inverse",
                                      help="inverse against geo center")

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           required=True,
                           help="input structure file")

    subparser.add_argument("-o",
                           "--output",
                           type=str,
                           required=True,
                           help="output structure file")

    subparser.add_argument("-c",
                           "--center",
                           type=str,
                           default="cell",
                           choices=["geo", "cell"],
                           help="inversion center, can geo or cell")

    # ---------------------------------------------------------------------------------
    # redefine lattice
    # ---------------------------------------------------------------------------------
    subparser = subparsers.add_parser("redefine", help="redefine lattice")

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           required=True,
                           help="input structure file")

    subparser.add_argument("-o",
                           "--output",
                           type=str,
                           required=True,
                           help="output structure file")

    subparser.add_argument("-a",
                           type=int,
                           nargs=3,
                           default=[1, 0, 0],
                           help="a from old a b c")

    subparser.add_argument("-b",
                           type=int,
                           nargs=3,
                           default=[0, 1, 0],
                           help="b from old a b c")

    subparser.add_argument("-c",
                           type=int,
                           nargs=3,
                           default=[0, 0, 1],
                           help="c from old a b c")

    subparser.add_argument(
        "--precision",
        type=float,
        default=1.0e-8,
        help=
        "a value that is less than 1 and infinitely close to 1 used to judge whether one atom is in another periodic of the redefined cell"
    )

    # ---------------------------------------------------------------------------------
    # cleave surface
    # ---------------------------------------------------------------------------------
    subparser = subparsers.add_parser("cleave", help="cleave surface")

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           required=True,
                           help="input structure file")

    subparser.add_argument("-o",
                           "--output",
                           type=str,
                           required=True,
                           help="output structure file")

    subparser.add_argument("--direction",
                           type=int,
                           nargs=3,
                           default=[0, 0, 1],
                           help="direction of the surface plane to cleave")

    subparser.add_argument(
        "--thick",
        type=float,
        help="thickness of the vacuum layer, in unit of Angstrom, default is 10"
    )

    subparser.add_argument(
        "--precision",
        type=float,
        default=1.0e-8,
        help=
        "a value that is large than 0 and infinitely close to 0 used to judge whether one atom is in another periodic of the redefined cell used in cleave surface"
    )

    # ---------------------------------------------------------------------------------
    # merge layers | ab plane
    # ---------------------------------------------------------------------------------
    subparser = subparsers.add_parser("merge", help="merge layers | ab plane")

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           nargs=2,
                           required=True,
                           help="input structure files")

    subparser.add_argument("-o",
                           "--output",
                           type=str,
                           required=True,
                           help="output structure file")

    #subparser.add_argument("--direction", type=int, nargs=3, default=[0, 0, 1],
    #        help="direction of the surface plane to cleave")

    subparser.add_argument(
        "--usecell",
        type=str,
        default="average",
        choices=["1", "2", "average"],
        help="use cell of structure 1 or 2 , otherwise average by default")

    subparser.add_argument(
        "--thick",
        type=float,
        help="thickness of the vacuum layer, in unit of Angstrom, default is 10"
    )

    subparser.add_argument(
        "--distance",
        type=float,
        help="distance between the layer, in unit of Angstrom, default is 3.4")

    # ---------------------------------------------------------------------------------
    # nanotube builder
    # ---------------------------------------------------------------------------------
    subparser = subparsers.add_parser(
        "tube",
        help=
        "nanotube along b direction(a must be perpendicular to b and ab is the surface plane)"
    )

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           required=True,
                           help="input structure files")

    subparser.add_argument("-o",
                           "--output",
                           type=str,
                           required=True,
                           help="output structure file")

    subparser.add_argument(
        "--plane",
        type=int,
        default=1,
        help="on which plane to add vacuum layer. 1: ab, 2: ac, 3: bc")

    subparser.add_argument(
        "--axis",
        type=str,
        default="b",
        choices=["a", "b", "c"],
        help="build nanotube along an axis parallel to axis specified")

    # -----------------------------------------------------------------------------------
    # set frac within zero and one
    # ------------------------------------------------------------------------------------
    subparser = subparsers.add_parser(
        "std", help="set fractional coordinates within zero and one")

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           required=True,
                           help="input structure file")

    subparser.add_argument("-o",
                           "--output",
                           type=str,
                           required=True,
                           help="output structure file")

    # ------------------------------------------------------------------------------------
    # generate series of cell volume changed structures
    # ------------------------------------------------------------------------------------
    subparser = subparsers.add_parser(
        "cv", help="generate series of cell volume changed structures")

    subparser.add_argument("-i",
                           "--input",
                           type=str,
                           required=True,
                           help="input structure file")

    subparser.add_argument("-d",
                           "--directory",
                           type=str,
                           default="./",
                           help="directory to put the generated structures")

    subparser.add_argument(
        "--range",
        type=float,
        nargs=3,
        default=[0.95, 1.05, 0.01],
        help="cell volume change ratio, default is [0.95, 1.05, 0.01]")

    # ==========================================================
    # transfer parameters from the arg subparser to static_run setting
    # ==========================================================

    args = parser.parse_args()

    # if no argument passed to matflow
    if len(sys.argv) == 1:
        # display help message when no args provided
        parser.print_help()
        sys.exit(1)

    if args.driver == "supercell":
        from pymatflow.base.xyz import base_xyz
        from pymatflow.structure.crystal import crystal

        a = read_structure(filepath=args.input)
        supercell = a.build_supercell(args.supern)
        new_structure = crystal()
        new_structure.get_cell_atoms(cell=supercell["cell"],
                                     atoms=supercell["atoms"])
        write_structure(structure=new_structure, filepath=args.output)

        print("=========================================================\n")
        print("              structflow supercell builder\n")
        print("---------------------------------------------------------\n")
        print("you are trying to bulid supercell from %s\n" % args.input)
        print("the output structure file is -> %s\n" % args.output)

    elif args.driver == "fix":
        # can only write xyz and poscar file
        a = read_structure(filepath=args.input)
        if args.fix != None:
            fix = args.fix
        elif args.around_z != None:
            atoms_index_from_1 = []
            for i in range(len(a.atoms)):
                if a.atoms[i].z > (args.around_z[0] +
                                   args.around_z[1]) and a.atoms[i].z < (
                                       args.around_z[0] + args.around_z[2]):
                    atoms_index_from_1.append(i + 1)
            fix = atoms_index_from_1
        else:
            fix = []

        if args.output.split(".")[-1] == "xyz":
            fix_str = ""
            for i in fix:
                fix_str += "%d " % i
            os.system("xyz-fix-atoms.py -i %s -o %s --fix %s" %
                      (args.input, args.output, fix_str))
        elif os.path.basename(args.output) == "POSCAR":
            from pymatflow.vasp.base.poscar import vasp_poscar
            for i in fix:
                a.atoms[i - 1].fix = [True, True, True]
            poscar = vasp_poscar()
            poscar.xyz.cell = a.cell
            poscar.xyz.atoms = a.atoms
            poscar.xyz.natom = len(poscar.xyz.atoms)
            poscar.xyz.set_species_number()  # needed for poscar output
            poscar.selective_dynamics = True
            with open(args.output, 'w') as fout:
                poscar.to_poscar(fout=fout, coordtype="Direct")
        else:
            print(
                "===============================================================\n"
            )
            print("                      WARNING !!!\n")
            print(
                "---------------------------------------------------------------\n"
            )
            print("structflow fix now only supports write of xyz and POSCAR\n")
            sys.exit(1)
        # output an xsd file with fixed atoms colored specifically so that user can check the atoms fixed
        from xml.etree.ElementTree import parse
        os.system("mkdir -p /tmp/structflow/fix")
        write_structure(a, filepath="/tmp/structflow/fix/tmp.xsd")
        # read xsd file
        xsd = parse("/tmp/structflow/fix/tmp.xsd")

        # ID of Atom3D in xsd file start from 4
        imap = xsd.getroot().find("AtomisticTreeRoot").find(
            "SymmetrySystem").find("MappingSet").find("MappingFamily").find(
                "IdentityMapping")
        atoms = imap.findall("Atom3d")
        if args.color == "white":
            RGB = [255, 255, 255]
        elif args.color == "red":
            RGB = [255, 0, 0]
        elif args.color == "green":
            RGB = [0, 255, 0]
        elif args.color == "blue":
            RGB = [0, 0, 255]
        else:
            RGB = [255, 255, 255]  # default

        for i in fix:
            atoms[i - 1].set("Color",
                             "%f, %f, %f, %f" % (RGB[0], RGB[1], RGB[2], 1))

        # write xsd file
        xsd.write(args.input + ".coloring.atoms.fixed.xsd")

    elif args.driver == "convert":
        # will convert file type according to the suffix of the specified input and output file

        a = read_structure(filepath=args.input)
        write_structure(structure=a, filepath=args.output)

        print("=========================================================\n")
        print("              structflow convert\n")
        print("---------------------------------------------------------\n")
        print("with the help from ase.io\n")

    elif args.driver == "kpath":
        if args.engine == "seekpath":
            os.system("kpath-xyz-seekpath.py -i %s -o %s" %
                      (args.input, args.output))
        else:
            pass
    elif args.driver == "move":
        from pymatflow.structure.tools import move_along
        # input structure
        a = read_structure(filepath=args.input)
        # move atoms
        print("=========================================================\n")
        print("                   structflow\n")
        print("----------------------------------------------------------\n")
        print("you are trying to move atoms:\n")
        print(args.atoms)
        for i in args.atoms:
            print("%d -> %s\n" % (i, a.atoms[i - 1].name))
        print("\n")
        print("along direction:\n")
        print(args.direction)
        print("\n")
        print("by length of -> %f, in unit of Angstrom\n" % args.disp)
        move_along(a,
                   atoms_to_move=[i - 1 for i in args.atoms],
                   direc=args.direction,
                   disp=args.disp)

        # output structure
        write_structure(structure=a, filepath=args.output)

    elif args.driver == "remove":
        from pymatflow.structure.tools import remove_atoms
        a = read_structure(filepath=args.input)
        # remove atoms
        print(
            "=======================================================================\n"
        )
        print("                       structflow\n")
        print(
            "-----------------------------------------------------------------------\n"
        )
        print(
            "you are trying to remove from %s the following list of atoms:\n" %
            args.input)
        print(args.atoms)
        if args.atoms != None:
            for i in args.atoms:
                print("%d -> %s\n" % (i, a.atoms[i - 1].name))
        else:
            pass
        print("\n")
        print("also the following elements will be removed:\n")
        print(args.elements)
        print("the output structure file is -> %s\n" % args.output)

        if args.atoms != None:
            remove_atoms(a, atoms_to_remove=[i - 1 for i in args.atoms])

        # we should first remove atoms specified by args.atoms
        # and remove atoms specified by args.elements
        # as remove atom will change the index of atom
        if args.elements != None:
            element_atoms_to_remove = []
            for i in range(len(a.atoms)):
                if a.atoms[i].name in args.elements:
                    element_atoms_to_remove.append(i)
            remove_atoms(a, atoms_to_remove=element_atoms_to_remove)

        # output structure
        write_structure(structure=a, filepath=args.output)

    elif args.driver == "vacuum":
        from pymatflow.structure.tools import vacuum_layer
        a = read_structure(filepath=args.input)
        # add vacuum layer
        print(
            "=======================================================================\n"
        )
        print("                       structflow\n")
        print(
            "-----------------------------------------------------------------------\n"
        )
        if args.plane == 1:
            plane = "ab"
        elif args.plane == 2:
            plane = "ac"
        elif args.plane == 3:
            plane = "bc"
        print(
            "you are trying to add vacuum layer of %f Angstrom on %s plane\n" %
            (args.thick, plane))
        print("from %s\n" % args.input)
        print("\n")
        print("the output structure file is -> %s\n" % args.output)

        vacuum_layer(a,
                     plane=args.plane,
                     thickness=args.thick if args.thick != None else 10.0)

        # output structure
        write_structure(structure=a, filepath=args.output)
    elif args.driver == "inverse":
        from pymatflow.structure.tools import inverse_geo_center
        from pymatflow.structure.tools import inverse_cell_center
        a = read_structure(filepath=args.input)
        print(
            "=======================================================================\n"
        )
        print("                       structflow\n")
        print(
            "-----------------------------------------------------------------------\n"
        )
        if args.center == "geo":
            print(
                "you are trying to inverse the system against the geometric center\n"
            )
        elif args.center == "cell":
            print(
                "you are trying to inverse the system against the cell center\n"
            )
        print("from %s\n" % args.input)
        print("\n")
        print("the output structure file is -> %s\n" % args.output)

        if args.center == "geo":
            inverse_geo_center(a)
        elif args.center == "cell":
            inverse_cell_center(a)

        # output structure
        write_structure(structure=a, filepath=args.output)
    elif args.driver == "redefine":
        from pymatflow.structure.tools import redefine_lattice
        a = read_structure(filepath=args.input)
        print(
            "=======================================================================\n"
        )
        print("                       structflow\n")
        print(
            "-----------------------------------------------------------------------\n"
        )
        print("you are trying to redefine the lattice\n")
        print("from %s\n" % args.input)
        print("\n")
        print("the output structure file is -> %s\n" % args.output)

        redefined = redefine_lattice(structure=a,
                                     a=args.a,
                                     b=args.b,
                                     c=args.c,
                                     precision=args.precision)

        # output structure
        write_structure(structure=redefined, filepath=args.output)
    elif args.driver == "cleave":
        from pymatflow.structure.tools import cleave_surface
        a = read_structure(filepath=args.input)
        print(
            "=======================================================================\n"
        )
        print("                       structflow\n")
        print(
            "-----------------------------------------------------------------------\n"
        )
        print("you are trying to cleave the surface of (%d, %d, %d)\n" %
              (args.direction[0], args.direction[1], args.direction[2]))
        print("from %s\n" % args.input)
        print("\n")
        print("the output structure file is -> %s\n" % args.output)

        cleaved = cleave_surface(
            structure=a,
            direction=args.direction,
            thickness=args.thick if args.thick != None else 10.0,
            precision=args.precision)

        # output structure
        write_structure(structure=cleaved, filepath=args.output)
    elif args.driver == "merge":
        from pymatflow.structure.tools import merge_layers
        a_list = []
        for i in range(2):
            a_list.append(read_structure(filepath=args.input[i]))
        print(
            "=======================================================================\n"
        )
        print("                       structflow\n")
        print(
            "-----------------------------------------------------------------------\n"
        )
        print("you are trying to merge layers on ab plane\n")
        print("from %s\n" % (args.input[0]))
        print("and %s\n" % (args.input[1]))
        print("\n")
        print("the output structure file is -> %s\n" % args.output)

        if args.usecell == "1":
            usecell = 1
        elif args.usecell == "2":
            usecell = 2
        else:
            usecell = "average"
        merged = merge_layers(
            structure1=a_list[0],
            structure2=a_list[1],
            use_cell=usecell,
            distance=args.distance if args.distance != None else 3.4,
            thickness=args.thick if args.thick != None else 10.0)

        # output structure
        write_structure(structure=merged, filepath=args.output)
    elif args.driver == "tube":
        a = read_structure(filepath=args.input)
        if args.plane == 1:
            plane = "ab"
        elif args.plane == 2:
            plane = "ac"
        elif args.plane == 3:
            plane = "bc"
        print(
            "=======================================================================\n"
        )
        print("                       structflow\n")
        print(
            "-----------------------------------------------------------------------\n"
        )
        print(
            "you are trying to build nanotube of %s plane along %s vector\n" %
            (plane, args.axis))
        print("from %s\n" % (args.input))
        print("the output structure file is -> %s\n" % args.output)
        tube = None
        if plane == "ab":
            from pymatflow.structure.tools import build_nanotube_ab
            if args.axis not in "ab":
                print(
                    "building nanotube of ab plane along axis parallel to c is unphysical!!!\n"
                )
                sys.exit()
            else:
                tube = build_nanotube_ab(structure=a, axis=args.axis)
        if plane == "ac":
            from pymatflow.structure.tools import build_nanotube_ac
            if args.axis not in "ac":
                print(
                    "building nanotube of ac plane along axis parallel to b is unphysical!!!\n"
                )
                sys.exit()
            else:
                tube = build_nanotube_ac(structure=a, axis=args.axis)
        if plane == "bc":
            from pymatflow.structure.tools import build_nanotube_bc
            if args.axis not in "bc":
                print(
                    "building nanotube of bc plane along axis parallel to a is unphysical!!!\n"
                )
                sys.exit()
            else:
                tube = build_nanotube_bc(structure=a, axis=args.axis)

        # output structure
        if tube != None:
            write_structure(structure=tube, filepath=args.output)
    elif args.driver == "std":
        from pymatflow.structure.tools import set_frac_within_zero_and_one
        a = read_structure(filepath=args.input)
        print(
            "=======================================================================\n"
        )
        print("                       structflow\n")
        print(
            "-----------------------------------------------------------------------\n"
        )
        print("you are trying to set fractional coords within 0 and 1\n")
        print("from %s\n" % (args.input))
        print("\n")
        print("the output structure file is -> %s\n" % args.output)

        normalized = set_frac_within_zero_and_one(structure=a)
        # output structure
        write_structure(structure=normalized, filepath=args.output)
    elif args.driver == "cv":
        from pymatflow.structure.crystal import crystal
        from pymatflow.base.atom import Atom
        a = read_structure(filepath=args.input)
        print(
            "=======================================================================\n"
        )
        print("                       structflow\n")
        print(
            "-----------------------------------------------------------------------\n"
        )
        print(
            "you are trying to get a series of structure with different volume\n"
        )
        print("from %s\n" % (args.input))
        print("\n")
        print("the output dir for structure file is -> %s\n" % args.directory)

        # now calc the fractional coordinates
        atoms_frac = []
        latcell = np.array(a.cell)
        convmat = np.linalg.inv(latcell.T)
        for i in range(len(a.atoms)):
            atom = []
            atom.append(a.atoms[i].name)
            atom = atom + list(
                convmat.dot(
                    np.array([a.atoms[i].x, a.atoms[i].y, a.atoms[i].z])))
            atoms_frac.append(atom)
        #
        out = crystal()
        os.system("mkdir -p %s" % args.directory)
        for i, ratio_v in enumerate(
                np.arange(args.range[0], args.range[1], args.range[2])):
            ratio = np.power(ratio_v, 1 / 3)

            # now convert coord of atom in atoms_frac_within_new_cell to cartesian
            out.atoms = []
            out.cell = (np.array(a.cell) * ratio).tolist()
            latcell = np.array(out.cell)
            convmat_frac_to_cartesian = latcell.T
            for atom in atoms_frac:
                cartesian = list(
                    convmat_frac_to_cartesian.dot(
                        np.array([atom[1], atom[2], atom[3]])))
                out.atoms.append(
                    Atom(name=atom[0],
                         x=cartesian[0],
                         y=cartesian[1],
                         z=cartesian[2]))
            output_name = ".".join(
                os.path.basename(args.input).split(".")[:-1] +
                ["%d" % i, "cif"])
            write_structure(out,
                            filepath=os.path.join(args.directory, output_name))
            #
        with open(os.path.join(args.directory, "log.txt"), 'w') as fout:
            fout.write("# index\tratio_v\tvolume(Angstrom^3)\n")
            for i, ratio_v in enumerate(
                    np.arange(args.range[0], args.range[1], args.range[2])):
                ratio = np.power(ratio_v, 1 / 3)
                cell_now = (np.array(a.cell) * ratio).tolist()
                fout.write("%d\t%f\t%f\n" %
                           (i, ratio_v, np.linalg.det(cell_now)))
Esempio n. 19
0
def merge_layers(structure1,
                 structure2,
                 use_cell=None,
                 distance=3.4,
                 thickness=10):
    """
    :param structure1: an instance of crystal()
    :param structure2: an instance of crystal()    
    :param use_cell: use cell parameter of structure 1 or 2 or None(average) to set the new a b cell parameter
        attention: c vector is not handled this way
        
    :param distance: the distance between layers
    :param thickness: the vaccum layer thickness of the combined system
    
    :return an object of crystal()
    Note:
        only merge layers with ab plane as the surface plane
    """
    from pymatflow.structure.crystal import crystal
    from pymatflow.base.atom import Atom

    structure1 = set_frac_within_zero_and_one(structure1)
    structure2 = set_frac_within_zero_and_one(structure2)

    old_cell_1 = copy.deepcopy(structure1.cell)
    old_cell_2 = copy.deepcopy(structure2.cell)

    # first transfer to fractional coordinate
    structure1.natom = len(structure1.atoms)
    frac_1 = structure1.get_fractional()
    structure2.natom = len(structure2.atoms)
    frac_2 = structure2.get_fractional()

    average_cell = []
    for i in range(3):
        average_cell.append(
            list((np.array(old_cell_1[i]) + np.array(old_cell_2[i])) / 2))

    out = crystal()

    if use_cell == 1:
        latcell_frac_to_cart_1 = old_cell_1
        latcell_frac_to_cart_2 = old_cell_1[0:2]
        latcell_frac_to_cart_2.append(old_cell_2[2])
    elif use_cell == 2:
        latcell_frac_to_cart_2 = old_cell_2
        latcell_frac_to_cart_1 = old_cell_2[0:2]
        latcell_frac_to_cart_1.append(old_cell_1[2])
    else:
        #average_ab = []
        #for i in range(2):
        #    vec = []
        #    for j in range(3):
        #        vec.append((old_cell_1[i][j] + old_cell_2[i][j]) / 2 )
        #    average_ab.append(vec)

        latcell_frac_to_cart_1 = average_cell
        latcell_frac_to_cart_1[2] = old_cell_1[2]
        latcell_frac_to_cart_2 = average_cell
        latcell_frac_to_cart_2[2] = old_cell_2[2]

    # convert frac to cartesian again
    convmat_1 = np.array(latcell_frac_to_cart_1).T
    convmat_2 = np.array(latcell_frac_to_cart_2).T

    cart_1 = []
    for atom in frac_1:
        cartesian = list(convmat_1.dot(np.array([atom[1], atom[2], atom[3]])))
        cart_1.append([atom[0], cartesian[0], cartesian[1], cartesian[2]])

    cart_2 = []
    for atom in frac_2:
        cartesian = list(convmat_2.dot(np.array([atom[1], atom[2], atom[3]])))
        cart_2.append([atom[0], cartesian[0], cartesian[1], cartesian[2]])

    # make distance gap between cart_1 and cart_2 is the value of distance
    z_1 = []
    for atom in cart_1:
        z_1.append(atom[3])
    z_2 = []
    for atom in cart_2:
        z_2.append(atom[3])
    max_z_1 = max(z_1)
    min_z_2 = min(z_2)

    for i in range(len(cart_2)):
        cart_2[i][3] += distance - (min_z_2 - max_z_1)

    cart_all = cart_1 + cart_2
    out.atoms = []
    for atom in cart_all:
        out.atoms.append(Atom(name=atom[0], x=atom[1], y=atom[2], z=atom[3]))

    if use_cell == 1:
        out.cell = old_cell_1
        factor = (np.linalg.norm(np.array(old_cell_1[2])) + np.linalg.norm(
            np.array(old_cell_2[2]))) / np.linalg.norm(np.array(old_cell_1[2]))
        out.cell[2] = list(np.array(old_cell_1[2]) * factor)
    elif use_cell == 2:
        out.cell = old_cell_2
        factor = (np.linalg.norm(np.array(old_cell_1[2])) + np.linalg.norm(
            np.array(old_cell_2[2]))) / np.linalg.norm(np.array(old_cell_2[2]))
        out.cell[2] = list(np.array(old_cell_2[2]) * factor)
    else:
        out.cell = average_cell
        factor = (np.linalg.norm(np.array(old_cell_1[2])) + np.linalg.norm(
            np.array(old_cell_2[2]))) / np.linalg.norm(np.array(out.cell[2]))
        out.cell[2] = list(np.array(out.cell[2]) * factor)

    vacuum_layer(structure=out, plane=1, thickness=thickness)

    return out