Ejemplo n.º 1
0
class Xyz(File):
    """Class for xyz files."""
    def __init__(self, absolute_path=None):
        """Initiates an Xyz object.
        If absolute_path is not None, reads the xyz file."""

        super().__init__(absolute_path)
        self.tags.add("xyz")
        self.atoms = Atoms([])

        if self.absolute_path is not None:
            self.read_xyz()

    def read_xyz(self):
        """
        Reads info from xyz file, whose path was given as
        absolute_path when the object was instantiated.

        Raises
        ------
        ValueError
            If the xyz file has extra lines between atoms.
            If a Lattice is found that has the wrong number of parameters.
        TypeError
            If non-numeric values are found for an atom's position.

        """
        self._check_read()

        lines = iter(self.content)
        line = next(lines)
        n_atoms = 0
        while True:
            try:
                n_atoms = int(line.split()[0])
            except TypeError:
                line = next(lines)
                continue
            else:
                break

        lattice_indexes = self._find("Lattice")
        must_invent_cell = False
        if len(lattice_indexes) > 0:
            lattice_index = lattice_indexes[0]
            lattice = find_between(self.content[lattice_index], '"', '"')
            try:
                xx, xy, xz, yx, yy, yz, zx, zy, zz = tuple(lattice.split())
            except ValueError:
                raise ValueError("Lattice in xyz file is bad")
            self.atoms.cell = [[xx, xy, xz], [yx, yy, yz], [zx, zy, zz]]
        else:  # if no "Lattice" is found, atoms.cell is invented below
            lattice_index = 0
            must_invent_cell = True

        for line in self.content[lattice_index + 1:]:
            if not line[0].isalpha():
                continue
            try:
                s, x, y, z = tuple(line.split())
            except ValueError:
                continue
                # raise ValueError("xyz file has a bad format, avoid extra lines")
            try:
                xyz = [float(x), float(y), float(z)]
            except TypeError:
                raise TypeError("bad positions in file: {} {} {}".format(
                    x, y, z))

            atom = Atom(atom_type=s, position=xyz)
            self.atoms.add_atom(atom)

        if len(self.atoms) != n_atoms:
            print("WARNING: {} atoms declared in file, {} atoms found in file".
                  format(n_atoms, len(self.atoms)))

        if must_invent_cell:
            gap = 10  # angstroms
            min_x = min(atom.position[0] for atom in self.atoms)
            max_x = max(atom.position[0] for atom in self.atoms)
            min_y = min(atom.position[1] for atom in self.atoms)
            max_y = max(atom.position[1] for atom in self.atoms)
            min_z = min(atom.position[2] for atom in self.atoms)
            max_z = max(atom.position[2] for atom in self.atoms)
            xx = max_x - min_x + gap
            yy = max_y - min_y + gap
            zz = max_z - min_z + gap
            self.atoms.cell = [[xx, 0, 0], [0, yy, 0], [0, 0, zz]]

    def write_xyz(self, filename, real_types=False, with_classification=False):
        """
        Writes xyz file with info present in the Xyz object.

        Parameters
        ----------
        filename : str
            Path to output xyz file.
        real_types : bool, optional
            If real types should be used instead of types
            (e.g. C instead of C2). Standard is False.
        with_classification : bool, optional
            If atom classification is wanted as a comment after every line.
            Standard is False.

        """

        with open(filename, "w") as F:
            F.write(str(len(self.atoms)))
            F.write('\n')

            if self.atoms.cell is not None:
                F.write('Lattice="')
                F.write(' '.join([str(p) for p in self.atoms.cell[0]]) + ' ')
                F.write(' '.join([str(p) for p in self.atoms.cell[1]]) + ' ')
                F.write(' '.join([str(p)
                                  for p in self.atoms.cell[2]]) + '"' + '\n')
            else:
                F.write('\n')

            if not with_classification:
                for atom in self.atoms:
                    F.write(atom.__str__(real_type=real_types) + '\n')
            else:
                for atom in self.atoms:
                    F.write(
                        atom.__str__(real_type=real_types) + '\t# ' +
                        atom.classification + '\n')

    def write_simple_cif(self,
                         filename,
                         struct_name,
                         comment=None,
                         centralize=True):
        """
        Writes a simple cif file. Meant to be used for 1D structures.

        Parameters
        ----------
        filename : str
            Path to output cif file.
        struct_name : str
            Name of the structure, to be included in the file info.
        comment : str, optional
            Comment to the structure, to be included in the file info.
            Standard comment is 'simple cif for 1D nanostructure'.
        centralize : bool, optional
            If the atomic positions should be centralized in the cell.
            Standard is True.

        Notes
        -----
        This is a very simple cif writing for theoretical structures.
        It's not meant to be used for complete crystallography information.

        """

        self._check_read()
        if not self.atoms.atoms:
            self.read_xyz()

        # arguments checking
        struct_name = struct_name.replace(" ", "")
        if comment is None:
            comment = "simple cif for 1D nanostructure"

        with open(filename, "w") as F:

            F.write("data_" + struct_name + "\n\n")

            F.write("_publ_section_comment" + "\n" + ";" + "\n" + comment +
                    "\n" + ";" + "\n\n")

            cell_x = self.atoms.cell[0][0]
            cell_y = self.atoms.cell[1][1]
            cell_z = self.atoms.cell[2][2]

            F.write("_cell_length_a " + str(round(cell_x, 4)) + "(0)" + "\n")
            F.write("_cell_length_b " + str(round(cell_y, 4)) + "(0)" + "\n")
            F.write("_cell_length_c " + str(round(cell_z, 4)) + "(0)" + "\n")
            F.write("_cell_angle_alpha 90.0000(0)" + "\n")
            F.write("_cell_angle_beta 90.0000(0)" + "\n")
            F.write("_cell_angle_gamma 90.0000(0)" + "\n\n")

            F.write("_symmetry_space_group_name_H-M 'P 1'" + "\n")
            F.write("_symmetry_Int_Tables_number 1" + "\n")
            F.write("_symmetry_cell_setting triclinic" + "\n\n")

            F.write("loop_" + "\n")
            F.write("_atom_site_label" + "\n")
            F.write("_atom_site_type_symbol" + "\n")
            F.write("_atom_site_occupancy" + "\n")
            F.write("_atom_site_fract_x" + "\n")
            F.write("_atom_site_fract_y" + "\n")
            F.write("_atom_site_fract_z" + "\n")

            if centralize:
                self.atoms.translate_to_cell_center()

            counter = dict()
            for atom in self.atoms:

                atom_type = str(atom.type)
                if atom_type in counter.keys():
                    counter[atom_type] += 1
                else:
                    counter[atom_type] = 1

                F.write(atom_type + str(counter[atom_type]) + " " +
                        atom.type.real + " 1.0000 " +
                        str(round(atom.position[0] / cell_x, 4)) + " " +
                        str(round(atom.position[1] / cell_y, 4)) + " " +
                        str(round(atom.position[2] / cell_z, 4)) + "\n")
Ejemplo n.º 2
0
class Cfg(File):
    """Class for LAMMPS cfg files."""

    # usual line: "mass type x y z *etc" where len(x,y,z,*etc) == entry_count

    def __init__(self,
                 absolute_path=None,
                 ignore_types=None):  # ignore_types is for internal use
        super().__init__(absolute_path=absolute_path)

        self.number_of_particles = None
        self.has_velocity = True
        self.atoms = Atoms([])
        self.entry_count = None
        self.auxiliary = []

        if ignore_types is None:
            self.ignore_types = []
        else:
            self.ignore_types = ignore_types

        if self.absolute_path is not None:
            self.read()

    def read(self):

        index_start = self._find("Number of particles")[0]
        index_auxiliary = None
        index_atoms = None
        self.number_of_particles = int(self.content[index_start].split()[-1])

        h0_11, h0_12, h0_13, h0_21, h0_22, h0_23, h0_31, h0_32, h0_33 = 0, 0, 0, 0, 0, 0, 0, 0, 0

        if len(self._find(".NO_VELOCITY.")) == 1:
            self.has_velocity = False

        if len(self._find("A = 1 Angstrom")) == 0:
            print(
                "WARNING: distance units in cfg file may not be in Angstroms!")

        for (index, line) in enumerate(self.content[index_start:],
                                       index_start):
            if line.startswith("H0"):
                if "H0(1,1)" in line:
                    h0_11 = float(line.split()[-2])
                elif "H0(1,2)" in line:
                    h0_12 = float(line.split()[-2])
                elif "H0(1,3)" in line:
                    h0_13 = float(line.split()[-2])
                elif "H0(2,1)" in line:
                    h0_21 = float(line.split()[-2])
                elif "H0(2,2)" in line:
                    h0_22 = float(line.split()[-2])
                elif "H0(2,3)" in line:
                    h0_23 = float(line.split()[-2])
                elif "H0(3,1)" in line:
                    h0_31 = float(line.split()[-2])
                elif "H0(3,2)" in line:
                    h0_32 = float(line.split()[-2])
                elif "H0(3,3)" in line:
                    h0_33 = float(line.split()[-2])
            elif "entry_count" in line:
                self.entry_count = int(line.split()[-1])
                n_standard_args = 6 if self.has_velocity else 3
                for _ in range(self.entry_count - n_standard_args):
                    self.auxiliary.append(None)
                index_auxiliary = index + 1
                break
            else:
                continue

        self.atoms.cell = [[h0_11, h0_12, h0_13], [h0_21, h0_22, h0_23],
                           [h0_31, h0_32, h0_33]]

        for (index, line) in enumerate(self.content[index_auxiliary:],
                                       index_auxiliary):
            if line.startswith("auxiliary"):
                i = int(find_between(line, "[", "]"))
                _, __, auxiliary = tuple(line.split())
                self.auxiliary[i] = auxiliary
            else:
                index_atoms = index
                break

        for i in range(index_atoms, len(self.content), 3):
            typ = clear_end(self.content[i + 1], [" ", "\n", "\t"])
            if typ in self.ignore_types:
                continue
            mass = float(self.content[i + 0])
            etc = dict()
            if self.has_velocity:
                raise TypeError("cfg with velocities! not implemented yet!")
            else:
                x, y, z, *args = tuple(self.content[i + 2].split())
                atom = Atom(atom_type=typ,
                            position=[float(x), float(y),
                                      float(z)])
                atom.position = np.matmul(self.atoms.cell, atom.position)
                atom.type.mass = mass
                for (index, arg) in enumerate(args):
                    etc[self.auxiliary[index]] = arg  # string
                atom.etc = etc
                self.atoms.add_atom(atom)

        if self.number_of_particles != len(
                self.atoms) and not self.ignore_types:
            print("WARNING: file says {} atoms, but only {} were found".format(
                self.number_of_particles, len(self.atoms)))

    def write_xyz(self, path, real_types=False):
        xyz = Xyz()
        xyz.atoms = self.atoms
        xyz.write_xyz(path, real_types=real_types)