Example #1
0
def lattice_2_lmpbox(lattice, origin=(0, 0, 0)):
    """
    Converts a lattice object to LammpsBox, and calculates the symmetry
    operation used.

    Args:
        lattice (Lattice): Input lattice.
        origin: A (3,) array/list of floats setting lower bounds of
            simulation box. Default to (0, 0, 0).

    Returns:
        LammpsBox, SymmOp

    """
    a, b, c = lattice.abc
    xlo, ylo, zlo = origin
    xhi = a + xlo
    if lattice.is_orthogonal:
        yhi = b + ylo
        zhi = c + zlo
        tilt = None
        rot_matrix = np.eye(3)
    else:
        m = lattice.matrix
        xy = np.dot(m[1], m[0] / a)
        yhi = np.sqrt(b**2 - xy**2) + ylo
        xz = np.dot(m[2], m[0] / a)
        yz = (np.dot(m[1], m[2]) - xy * xz) / (yhi - ylo)
        zhi = np.sqrt(c**2 - xz**2 - yz**2) + zlo
        tilt = [xy, xz, yz]
        rot_matrix = np.linalg.solve(
            [[xhi - xlo, 0, 0], [xy, yhi - ylo, 0], [xz, yz, zhi - zlo]], m)
    bounds = [[xlo, xhi], [ylo, yhi], [zlo, zhi]]
    symmop = SymmOp.from_rotation_and_translation(rot_matrix, origin)
    return LammpsBox(bounds, tilt), symmop
Example #2
0
def lattice_2_lmpbox(lattice, origin=(0, 0, 0)):
    """
    Converts a lattice object to LammpsBox, and calculates the symmetry
    operation used.

    Args:
        lattice (Lattice): Input lattice.
        origin: A (3,) array/list of floats setting lower bounds of
            simulation box. Default to (0, 0, 0).

    Returns:
        LammpsBox, SymmOp

    """
    a, b, c = lattice.abc
    xlo, ylo, zlo = origin
    xhi = a + xlo
    m = lattice.matrix
    xy = np.dot(m[1], m[0] / a)
    yhi = np.sqrt(b ** 2 - xy ** 2) + ylo
    xz = np.dot(m[2], m[0] / a)
    yz = (np.dot(m[1], m[2]) - xy * xz) / (yhi - ylo)
    zhi = np.sqrt(c ** 2 - xz ** 2 - yz ** 2) + zlo
    tilt = None if lattice.is_orthogonal else [xy, xz, yz]
    rot_matrix = np.linalg.solve([[xhi - xlo, 0, 0],
                                  [xy, yhi - ylo, 0],
                                  [xz, yz, zhi - zlo]], m)
    bounds = [[xlo, xhi], [ylo, yhi], [zlo, zhi]]
    symmop = SymmOp.from_rotation_and_translation(rot_matrix, origin)
    return LammpsBox(bounds, tilt), symmop
Example #3
0
    def from_structure(cls, structure, ff_elements=None, atom_style="charge"):
        """
        Simple constructor building LammpsData from a structure without
        force field parameters and topologies.

        Args:
            structure (Structure): Input structure.
            ff_elements ([str]): List of strings of elements that must
                be present due to force field settings but not
                necessarily in the structure. Default to None.
            atom_style (str): Choose between "atomic" (neutral) and
            "charge" (charged). Default to "charge".

        """
        s = structure.get_sorted_structure()
        box, symmop = lattice_2_lmpbox(s.lattice)
        coords = symmop.operate_multi(s.cart_coords)
        site_properties = s.site_properties
        if "velocities" in site_properties:
            velos = np.array(s.site_properties["velocities"])
            rot = SymmOp.from_rotation_and_translation(symmop.rotation_matrix)
            rot_velos = rot.operate_multi(velos)
            site_properties.update({"velocities": rot_velos})
        boxed_s = Structure(box.to_lattice(),
                            s.species,
                            coords,
                            site_properties=site_properties,
                            coords_are_cartesian=True)

        symbols = list(s.symbol_set)
        if ff_elements:
            symbols.extend(ff_elements)
        elements = sorted(Element(el) for el in set(symbols))
        mass_info = [tuple([i.symbol] * 2) for i in elements]
        ff = ForceField(mass_info)
        topo = Topology(boxed_s)
        return cls.from_ff_and_topologies(box=box,
                                          ff=ff,
                                          topologies=[topo],
                                          atom_style=atom_style)
Example #4
0
    def from_structure(cls, structure, ff_elements=None, atom_style="charge"):
        """
        Simple constructor building LammpsData from a structure without
        force field parameters and topologies.

        Args:
            structure (Structure): Input structure.
            ff_elements ([str]): List of strings of elements that must
                be present due to force field settings but not
                necessarily in the structure. Default to None.
            atom_style (str): Choose between "atomic" (neutral) and
            "charge" (charged). Default to "charge".

        """
        s = structure.get_sorted_structure()
        box, symmop = lattice_2_lmpbox(s.lattice)
        coords = symmop.operate_multi(s.cart_coords)
        site_properties = s.site_properties
        if "velocities" in site_properties:
            velos = np.array(s.site_properties["velocities"])
            rot = SymmOp.from_rotation_and_translation(symmop.rotation_matrix)
            rot_velos = rot.operate_multi(velos)
            site_properties.update({"velocities": rot_velos})
        boxed_s = Structure(box.to_lattice(), s.species, coords,
                            site_properties=site_properties,
                            coords_are_cartesian=True)

        symbols = list(s.symbol_set)
        if ff_elements:
            symbols.extend(ff_elements)
        elements = sorted(Element(el) for el in set(symbols))
        mass_info = [tuple([i.symbol] * 2) for i in elements]
        ff = ForceField(mass_info)
        topo = Topology(boxed_s)
        return cls.from_ff_and_topologies(box=box, ff=ff, topologies=[topo],
                                          atom_style=atom_style)
Example #5
0
def genacomp(initialstructure,
             savedir,
             A1='Co',
             A2='Mn',
             fixspecies='Ni',
             initiallayers=9):
    # step 1 - load structure and half it (allows easier symmterisation of surfaces)
    # initialstructure = 'C:/Users/Bud/Desktop/test/104vac11/sup141Co4Mnsurfsub/POSCAR'
    obby = Structure.from_file(initialstructure)
    # A1 = 'Co'
    # A2 = 'Mn'
    # fixspecies = 'Ni'
    # initiallayers = 5
    cdim = []
    for element in obby:
        cdim.append(element.coords[2])
    cdim.sort()
    # TODO - Add this improved method to the dyna package. it seems to be more flexible.
    listy = []
    ranvar = 0.01
    while len(listy) != initiallayers:
        listy = [
            list(g) for k, g in itt.groupby(cdim, partial(the_key, ranvar))
        ]
        ranvar = ranvar * 1.01
        print(ranvar)
    keepcount = math.ceil(initiallayers / 2)

    # need to half the listy here
    listy = listy[0:keepcount]

    flat_list = [item for sublist in listy for item in sublist]
    setter = list(set(flat_list))
    cutstru = []
    for c in setter:
        for k in obby:
            print(k)
            if c == k.coords[2]:
                print('yes')
                cutstru.append(k)

    newstruc = Structure.from_sites(cutstru)
    # now that structure has been halved-ish, it'll be ideal to count the number of sites!
    print('total number of sites = ' + str(newstruc.num_sites))

    ### Time to generate all possible combinations! - detect prepresentdefect
    counter = 0
    it = 0
    fixlist = []
    while it < newstruc.species.__len__():
        if newstruc.species[it].name == fixspecies:
            print('species found - ' + fixspecies + ' @ ' +
                  str(newstruc.frac_coords[it]))
            fixlist.append(newstruc[it])
            counter += 1
        it += 1
    newstruc.remove_species([fixspecies])

    # find A1 and A2 total count!
    metal = 0
    it = 0
    itlist = []
    while it < newstruc.species.__len__():
        if newstruc.species[it].name == A1:
            print('species found - ' + A1 + ' @ ' +
                  str(newstruc.frac_coords[it]))
            itlist.append(newstruc[it])
            metal += 1
        elif newstruc.species[it].name == A2:
            print('species found - ' + A1 + ' @ ' +
                  str(newstruc.frac_coords[it]))
            itlist.append(newstruc[it])
            metal += 1
        it += 1

    print('total changable sites is ' + str(metal))
    if metal > 7:
        print("that's a lot of sites do you want to proceed")
        proceed = input('y/n')
        if proceed != 'y':
            print('good idea, your computer will have been sad')
            exit()

    # Need to now split this list into a numerous lists of all possible combos.
    # step 1, generate the pure A1 metal if it isn't already
    changestru = newstruc.copy()
    for i in itlist:
        i.species = A1
        changestru.remove(i)

    allset = list(itt.product([A1, A2], repeat=metal))
    county = 0
    for combo in allset:
        iterstru = changestru.copy()
        i = 0
        while i < len(itlist):
            itlist[i].species = combo[i]
            i += 1
        makelist = itlist + fixlist + list(changestru)
        # this is 1 half of the structure.
        savenew = Structure.from_sites(makelist)

        # making the otherside
        cdim = []
        for element in makelist:
            cdim.append(element.coords[2])
        cdim.sort()

        listy = []
        ranvar = 0.01
        while len(listy) != math.ceil(initiallayers / 2):
            listy = [
                list(g) for k, g in itt.groupby(cdim, partial(the_key, ranvar))
            ]
            ranvar = ranvar * 1.01
            print(ranvar)
        # need to half the listy here
        listy.pop()
        flat_list = [item for sublist in listy for item in sublist]
        setter = list(set(flat_list))
        cutstru = []
        for c in setter:
            for k in makelist:
                print(k)
                if c == k.coords[2]:
                    print('yes')
                    cutstru.append(k)
        strr = Structure.from_sites(cutstru, to_unit_cell=True)
        strr.apply_operation(
            SymmOp.reflection((0, 0, 1),
                              origin=(strr.lattice.a / 2, strr.lattice.b / 2,
                                      strr.lattice.c / 2)))

        newest = list(strr) + list(savenew)
        both = Structure.from_sites(newest)
        for element in both:
            if element.coords[2] < 0:
                element.coords[2] = abs(element.coords[2])
                element.frac_coords[2] = abs(element.frac_coords[2])
            if element.frac_coords[2] < 0:
                element.frac_coords[2] = abs(element.frac_coords[2])
        both.sort()
        os.makedirs(savedir + '/' + A2 + str(int(both.composition.get(A2))) +
                    '_' + str(county),
                    exist_ok=True)
        both.to(filename=(savedir + "/" + A2 +
                          str(int(both.composition.get(A2))) + '_' +
                          str(county) + '/POSCAR'))
        county += 1
Example #6
0
    def _parse_molecule(cls, contents):
        """
        Helper method to parse coordinates of Molecule. Copied from GaussianInput class.
        """
        paras = {}
        var_pattern = re.compile("^([A-Za-z]+\S*)[\s=,]+([\d\-\.]+)$")
        for l in contents:
            m = var_pattern.match(l.strip())
            if m:
                paras[m.group(1)] = float(m.group(2))

        species = []
        coords = []
        # Stores whether a Zmatrix format is detected. Once a zmatrix format
        # is detected, it is assumed for the remaining of the parsing.
        zmode = False
        for l in contents:
            l = l.strip()
            if not l:
                break
            if (not zmode) and cls.xyz_patt.match(l):
                m = cls.xyz_patt.match(l)
                species.append(m.group(1))
                toks = re.split("[,\s]+", l.strip())
                if len(toks) > 4:
                    coords.append(list(map(float, toks[2:5])))
                else:
                    coords.append(list(map(float, toks[1:4])))
            elif cls.zmat_patt.match(l):
                zmode = True
                toks = re.split("[,\s]+", l.strip())
                species.append(toks[0])
                toks.pop(0)
                if len(toks) == 0:
                    coords.append(np.array([0.0, 0.0, 0.0]))
                else:
                    nn = []
                    parameters = []
                    while len(toks) > 1:
                        ind = toks.pop(0)
                        data = toks.pop(0)
                        try:
                            nn.append(int(ind))
                        except ValueError:
                            nn.append(species.index(ind) + 1)
                        try:
                            val = float(data)
                            parameters.append(val)
                        except ValueError:
                            if data.startswith("-"):
                                parameters.append(-paras[data[1:]])
                            else:
                                parameters.append(paras[data])
                    if len(nn) == 1:
                        coords.append(np.array(
                            [0.0, 0.0, float(parameters[0])]))
                    elif len(nn) == 2:
                        coords1 = coords[nn[0] - 1]
                        coords2 = coords[nn[1] - 1]
                        bl = parameters[0]
                        angle = parameters[1]
                        axis = [0, 1, 0]
                        op = SymmOp.from_origin_axis_angle(coords1, axis,
                                                           angle, False)
                        coord = op.operate(coords2)
                        vec = coord - coords1
                        coord = vec * bl / np.linalg.norm(vec) + coords1
                        coords.append(coord)
                    elif len(nn) == 3:
                        coords1 = coords[nn[0] - 1]
                        coords2 = coords[nn[1] - 1]
                        coords3 = coords[nn[2] - 1]
                        bl = parameters[0]
                        angle = parameters[1]
                        dih = parameters[2]
                        v1 = coords3 - coords2
                        v2 = coords1 - coords2
                        axis = np.cross(v1, v2)
                        op = SymmOp.from_origin_axis_angle(
                            coords1, axis, angle, False)
                        coord = op.operate(coords2)
                        v1 = coord - coords1
                        v2 = coords1 - coords2
                        v3 = np.cross(v1, v2)
                        adj = get_angle(v3, axis)
                        axis = coords1 - coords2
                        op = SymmOp.from_origin_axis_angle(
                            coords1, axis, dih - adj, False)
                        coord = op.operate(coord)
                        vec = coord - coords1
                        coord = vec * bl / np.linalg.norm(vec) + coords1
                        coords.append(coord)

        def parse_species(sp_str):
            """
            The species specification can take many forms. E.g.,
            simple integers representing atomic numbers ("8"),
            actual species string ("C") or a labelled species ("C1").
            Sometimes, the species string is also not properly capitalized,
            e.g, ("c1"). This method should take care of these known formats.
            """
            try:
                return int(sp_str)
            except ValueError:
                sp = re.sub("\d", "", sp_str)
                return sp.capitalize()

        species = list(map(parse_species, species))

        return Molecule(species, coords)
Example #7
0
    def __init__(self, struct, symprec=None, charges=None):
        """
        A wrapper around CifFile to write CIF files from pymatgen structures.

        Args:
            struct (Structure): structure to write
            symprec (float): If not none, finds the symmetry of the structure
                and writes the cif with symmetry information. Passes symprec
                to the SpacegroupAnalyzer
            write_magmoms (bool): If True, will write magCIF file. Incompatible
                with symprec
        """

        format_str = "{:.8f}"

        block = OrderedDict()
        loops = []
        spacegroup = ("P 1", 1)
        if symprec is not None:
            sf = SpacegroupAnalyzer(struct, symprec)
            spacegroup = (sf.get_space_group_symbol(),
                          sf.get_space_group_number())
            # Needs the refined struture when using symprec. This converts
            # primitive to conventional structures, the standard for CIF.
            struct = sf.get_refined_structure()

        latt = struct.lattice
        comp = struct.composition
        no_oxi_comp = comp.element_composition
        block["_symmetry_space_group_name_H-M"] = spacegroup[0]
        for cell_attr in ['a', 'b', 'c']:
            block["_cell_length_" + cell_attr] = format_str.format(
                getattr(latt, cell_attr))
        for cell_attr in ['alpha', 'beta', 'gamma']:
            block["_cell_angle_" + cell_attr] = format_str.format(
                getattr(latt, cell_attr))
        block["_symmetry_Int_Tables_number"] = spacegroup[1]
        block["_chemical_formula_structural"] = no_oxi_comp.reduced_formula
        block["_chemical_formula_sum"] = no_oxi_comp.formula
        block["_cell_volume"] = "%.8f" % latt.volume

        reduced_comp, fu = no_oxi_comp.get_reduced_composition_and_factor()
        block["_cell_formula_units_Z"] = str(int(fu))

        if symprec is None:
            block["_symmetry_equiv_pos_site_id"] = ["1"]
            block["_symmetry_equiv_pos_as_xyz"] = ["x, y, z"]
        else:
            sf = SpacegroupAnalyzer(struct, symprec)

            symmops = []
            for op in sf.get_symmetry_operations():
                v = op.translation_vector
                symmops.append(
                    SymmOp.from_rotation_and_translation(
                        op.rotation_matrix, v))

            ops = [op.as_xyz_string() for op in symmops]
            block["_symmetry_equiv_pos_site_id"] = \
                ["%d" % i for i in range(1, len(ops) + 1)]
            block["_symmetry_equiv_pos_as_xyz"] = ops

        loops.append(
            ["_symmetry_equiv_pos_site_id", "_symmetry_equiv_pos_as_xyz"])

        try:
            symbol_to_oxinum = OrderedDict([(el.__str__(), float(el.oxi_state))
                                            for el in sorted(comp.elements)])
            block["_atom_type_symbol"] = symbol_to_oxinum.keys()
            block["_atom_type_oxidation_number"] = symbol_to_oxinum.values()
            loops.append(["_atom_type_symbol", "_atom_type_oxidation_number"])
        except (TypeError, AttributeError):
            symbol_to_oxinum = OrderedDict([(el.symbol, 0)
                                            for el in sorted(comp.elements)])

        atom_site_type_symbol = []
        atom_site_symmetry_multiplicity = []
        atom_site_fract_x = []
        atom_site_fract_y = []
        atom_site_fract_z = []
        atom_site_label = []
        atom_site_occupancy = []
        atom_site_charge_label = []

        count = 1
        if symprec is None:
            for site in struct:
                for sp, occu in sorted(site.species_and_occu.items()):
                    atom_site_type_symbol.append(sp.__str__())
                    atom_site_symmetry_multiplicity.append("1")
                    atom_site_fract_x.append("{0:f}".format(site.a))
                    atom_site_fract_y.append("{0:f}".format(site.b))
                    atom_site_fract_z.append("{0:f}".format(site.c))
                    atom_site_label.append("{}{}".format(sp.symbol, count))
                    atom_site_occupancy.append(occu.__str__())

                    count += 1
        else:
            # The following just presents a deterministic ordering.
            unique_sites = [
                (sorted(sites,
                        key=lambda s: tuple([abs(x)
                                             for x in s.frac_coords]))[0],
                 len(sites))
                for sites in sf.get_symmetrized_structure().equivalent_sites
            ]
            for site, mult in sorted(unique_sites,
                                     key=lambda t:
                                     (t[0].species_and_occu.average_electroneg,
                                      -t[1], t[0].a, t[0].b, t[0].c)):
                for sp, occu in site.species_and_occu.items():
                    atom_site_type_symbol.append(sp.__str__())
                    atom_site_symmetry_multiplicity.append("%d" % mult)
                    atom_site_fract_x.append("{0:f}".format(site.a))
                    atom_site_fract_y.append("{0:f}".format(site.b))
                    atom_site_fract_z.append("{0:f}".format(site.c))
                    atom_site_label.append("{}{}".format(sp.symbol, count))
                    atom_site_occupancy.append(occu.__str__())
                    count += 1

        block["_atom_site_type_symbol"] = atom_site_type_symbol
        block["_atom_site_label"] = atom_site_label
        block["_atom_site_symmetry_multiplicity"] = \
            atom_site_symmetry_multiplicity
        block["_atom_site_fract_x"] = atom_site_fract_x
        block["_atom_site_fract_y"] = atom_site_fract_y
        block["_atom_site_fract_z"] = atom_site_fract_z
        block["_atom_site_occupancy"] = atom_site_occupancy
        block["_atom_site_charge"] = charges

        loops.append([
            "_atom_site_type_symbol",
            "_atom_site_label",
            "_atom_site_symmetry_multiplicity",
            "_atom_site_fract_x",
            "_atom_site_fract_y",
            "_atom_site_fract_z",
            "_atom_site_occupancy",
            "_atom_site_charge",
        ])

        d = OrderedDict()
        d[comp.reduced_formula] = CifBlock(block, loops, comp.reduced_formula)
        self._cf = CifFile(d)