示例#1
0
    def _get_azimuth_elev(self, miller_index):
        """
        Args:
            miller_index: viewing direction

        Returns:
            azim, elev for plotting
        """
        if miller_index in [(0, 0, 1), (0, 0, 0, 1)]:
            return 0, 90
        cart = self.lattice.get_cartesian_coords(miller_index)
        azim = get_angle([cart[0], cart[1], 0], (1, 0, 0))
        v = [cart[0], cart[1], 0]
        elev = get_angle(cart, v)
        return azim, elev
def Bond_angle(pmg_struct, label2site_index, atom1_label,atom2_label,atom3_label):
    '''
    input: atom1_label, atom2_label, atom3_label -- str,  Ex) Fe5, O1, etc. 
            label2site_index -- dictionary defined in script
            pmg_struct -- pymatgen structure
    output: angle -- float, Angle of (atom1-atom2-atom3) will be calculated.
        Angle is calculated by using shortest vector of (atom1-atom2) and 
        (atom3-atom2) within periodic boundary condition.
    '''
    atom1_pmg_index=convert_site_index(label2site_index, atom1_label)
    atom2_pmg_index=convert_site_index(label2site_index, atom2_label)
    atom3_pmg_index=convert_site_index(label2site_index, atom3_label)
        
    # Get fractional coordinates for each atoms
    atom1_fcoords=pmg_struct.sites[atom1_pmg_index].frac_coords
    atom2_fcoords=pmg_struct.sites[atom2_pmg_index].frac_coords
    atom3_fcoords=pmg_struct.sites[atom3_pmg_index].frac_coords
    # pbc_shortest_vector from atom2 to atom1
    vector1=pbc_shortest_vectors(pmg_struct.lattice,atom2_fcoords,atom1_fcoords)
    # pbc_shortest_vector from atom2 to atom3
    vector2=pbc_shortest_vectors(pmg_struct.lattice,atom2_fcoords,atom3_fcoords)
    
    # angle betseen two vectors
    # pbc_shortest_vector can digest set of sites,
    #but here we only use one 1 site to get angle.
    angle=get_angle(vector1[0][0],vector2[0][0])
    return angle
示例#3
0
    def get_angle(self, s_index, i, j, k):
        """
        Returns **Minimum Image** angle specified by three sites.

        Args:
            s_index: Structure index in structures list
            i (int): Index of first site.
            j (int): Index of second site.
            k (int): Index of third site.

        Returns:
            (float) Angle in degrees.
        """
        structure = self.structures[s_index]
        lat_vec = np.array([structure.lattice.a, structure.lattice.b, structure.lattice.c])

        v1 = structure[i].coords - structure[j].coords
        v2 = structure[k].coords - structure[j].coords

        for v in range(3):
            if np.fabs(v1[v]) > lat_vec[v] / 2.0:
                v1[v] -= np.sign(v1[v]) * lat_vec[v]
            if np.fabs(v2[v]) > lat_vec[v] / 2.0:
                v2[v] -= np.sign(v2[v]) * lat_vec[v]
        return get_angle(v1, v2, units="degrees")
示例#4
0
    def _get_azimuth_elev(self, miller_index):
        """
        Args:
            miller_index: viewing direction

        Returns:
            azim, elev for plotting
        """
        if miller_index == (0, 0, 1) or miller_index == (0, 0, 0, 1):
            return 0, 90
        else:
            cart = self.lattice.get_cartesian_coords(miller_index)
            azim = get_angle([cart[0], cart[1], 0], (1, 0, 0))
            v = [cart[0], cart[1], 0]
            elev = get_angle(cart, v)
            return azim, elev
示例#5
0
def get_bond_angles(voronoi_neighbor_pairs):
    bond_angles_avg = []
    bond_angles_std = []
    for center_site, neighbor_sites in voronoi_neighbor_pairs.items():
        local_bond_angles = []
        for neighbor1_site, neighbor2_site in neighbor_sites:
            neighbor1_vector = neighbor1_site.coords - center_site.coords
            neighbor2_vector = neighbor2_site.coords - center_site.coords
            local_bond_angles.append(
                get_angle(neighbor1_vector, neighbor2_vector, units="degrees"))
        bond_angles_avg.append(np.mean(local_bond_angles))
        bond_angles_std.append(np.std(
            local_bond_angles))  # sample stdev (for population, set ddof=1)
    return (bond_angles_avg, bond_angles_std)
示例#6
0
文件: utils.py 项目: albalu/pymatgen
    def _align_monomer(self, monomer, mon_vector, move_direction):
        """
        rotate the monomer so that it is aligned along the move direction

        Args:
            monomer (Molecule)
            mon_vector (numpy.array): molecule vector that starts from the
                start atom index to the end atom index
            move_direction (numpy.array): the direction of the polymer chain
                extension
        """
        axis = np.cross(mon_vector, move_direction)
        origin = monomer[self.start].coords
        angle = get_angle(mon_vector, move_direction)
        op = SymmOp.from_origin_axis_angle(origin, axis, angle)
        monomer.apply_operation(op)
示例#7
0
    def _align_monomer(self, monomer, mon_vector, move_direction):
        """
        rotate the monomer so that it is aligned along the move direction

        Args:
            monomer (Molecule)
            mon_vector (numpy.array): molecule vector that starts from the
                start atom index to the end atom index
            move_direction (numpy.array): the direction of the polymer chain
                extension
        """
        axis = np.cross(mon_vector, move_direction)
        origin = monomer[self.start].coords
        angle = get_angle(mon_vector, move_direction)
        op = SymmOp.from_origin_axis_angle(origin, axis, angle)
        monomer.apply_operation(op)
示例#8
0
def check_and_force_collinear(mypatstructure, angle_tolerance=10.0):
    """Make sure the list of magmoms use the same spin axis by taking the
    largest magnetic moments as a reference axis for collinear calculations
    
    Parameters:
    -----------
    mypatstructure: Structure
                    structure object
    angle_tolerance: float
                     the minimum angle to consider between the reference spin axis and
                     magnetic moments vector. Default=10.
    Returns:
    --------
           all_true : boolean, if True the angle is within the threshold
    """
    from pymatgen.electronic_structure.core import Magmom
    from pymatgen.util.coord import get_angle

    magmoms = mypatstructure.site_properties['magmom']
    cif_moments, direction = Magmom.get_consistent_set_and_saxis(magmoms)
    #print("direction", direction)
    magmoms_ = np.empty([0,3])
    for comp in magmoms:
        magmoms_ = np.vstack([magmoms_, [comp[0], comp[1], comp[2]]])
        #print("comp", comp[0], comp[1], comp[2])

    # filter non zero moments
    magmoms_nzr = [m for m in magmoms_ if abs(np.any(m))]
    true_or_false = Magmom.are_collinear(magmoms)
    #if true_or_false == False:

    angles = np.empty([0, 1])
    for m in magmoms_nzr:
        angles = np.vstack([angles, get_angle(unit_vector(m), direction, units="degrees")])
        #print("angles", angles)

    store_check = []
    for angle in angles:
        store_check.append(check_angle_bool(angle, angle_tolerance))

    all_true = np.allclose(True, store_check)
    return all_true
示例#9
0
 def cover_surface(self, site_indices):
     """
     puts the ligand molecule on the given list of site indices
     """
     num_atoms = len(self.ligand)
     normal = self.normal
     # get a vector that points from one atom in the botton plane
     # to one atom on the top plane. This is required to make sure
     # that the surface normal points outwards from the surface on
     #  to which we want to adsorb the ligand
     vec_vac = self.cart_coords[self.top_atoms[0]] - \
         self.cart_coords[self.bottom_atoms[0]]
     # mov_vec = the vector along which the ligand will be displaced
     mov_vec = normal * self.displacement
     angle = get_angle(vec_vac, self.normal)
     # flip the orientation of normal if it is not pointing in
     # the right direction.
     if (angle > 90):
         normal_frac = self.lattice.get_fractional_coords(normal)
         normal_frac[2] = -normal_frac[2]
         normal = self.lattice.get_cartesian_coords(normal_frac)
         mov_vec = normal * self.displacement
     # get the index corresponding to the given atomic species in
     # the ligand that will bond with the surface on which the
     # ligand will be adsorbed
     adatom_index = self.get_index(self.adatom_on_lig)
     adsorbed_ligands_coords = []
     # set the ligand coordinates for each adsorption site on
     # the surface
     for sindex in site_indices:
         # align the ligand wrt the site on the surface to which
         # it will be adsorbed
         origin = self.cart_coords[sindex]
         self.ligand.translate_sites(list(range(num_atoms)),
                                     origin - self.ligand[
                                         adatom_index].coords)
         # displace the ligand by the given amount in the direction
         # normal to surface
         self.ligand.translate_sites(list(range(num_atoms)), mov_vec)
         # vector pointing from the adatom_on_lig to the
         # ligand center of mass
         vec_adatom_cm = self.ligand.center_of_mass - \
             self.ligand[adatom_index].coords
         # rotate the ligand with respect to a vector that is
         # normal to the vec_adatom_cm and the normal to the surface
         # so that the ligand center of mass is aligned along the
         # outward normal to the surface
         origin = self.ligand[adatom_index].coords
         angle = get_angle(vec_adatom_cm, normal)
         if 1 < abs(angle % 180) < 179:
             # For angles which are not 0 or 180,
             # perform a rotation about the origin along an axis
             # perpendicular to both bonds to align bonds.
             axis = np.cross(vec_adatom_cm, normal)
             op = SymmOp.from_origin_axis_angle(origin, axis, angle)
             self.ligand.apply_operation(op)
         elif abs(abs(angle) - 180) < 1:
             # We have a 180 degree angle.
             # Simply do an inversion about the origin
             for i in range(len(self.ligand)):
                 self.ligand[i] = (self.ligand[i].species_and_occu,
                                   origin - (
                                       self.ligand[i].coords - origin))
         # x - y - shifts
         x = self.x_shift
         y = self.y_shift
         rot = self.rot
         if x:
             self.ligand.translate_sites(list(range(num_atoms)),
                                         np.array([x, 0, 0]))
         if y:
             self.ligand.translate_sites(list(range(num_atoms)),
                                         np.array([0, y, 0]))
         if rot:
             self.ligand.apply_operation(
                 SymmOp.from_axis_angle_and_translation(
                     (1, 0, 0), rot[0], angle_in_radians=False,
                     translation_vec=(0, 0, 0)))
             self.ligand.apply_operation(
                 SymmOp.from_axis_angle_and_translation(
                     (0, 1, 0), rot[1], angle_in_radians=False,
                     translation_vec=(0, 0, 0)))
             self.ligand.apply_operation(
                 SymmOp.from_axis_angle_and_translation(
                     (0, 0, 1), rot[2], angle_in_radians=False,
                     translation_vec=(0, 0, 0)))
         # 3d numpy array
         adsorbed_ligands_coords.append(self.ligand.cart_coords)
         # extend the slab structure with the adsorbant atoms
     adsorbed_ligands_coords = np.array(adsorbed_ligands_coords)
     for j in range(len(site_indices)):
         [self.append(self.ligand.species_and_occu[i],
                      adsorbed_ligands_coords[j, i, :],
                      coords_are_cartesian=True)
          for i in range(num_atoms)]
示例#10
0
 def cover_surface(self, site_indices):
     """
     puts the ligand molecule on the given list of site indices
     """
     num_atoms = len(self.ligand)
     normal = self.normal
     # get a vector that points from one atom in the botton plane
     # to one atom on the top plane. This is required to make sure
     # that the surface normal points outwards from the surface on
     #  to which we want to adsorb the ligand
     vec_vac = self.cart_coords[self.top_atoms[0]] - \
         self.cart_coords[self.bottom_atoms[0]]
     # mov_vec = the vector along which the ligand will be displaced
     mov_vec = normal * self.displacement
     angle = get_angle(vec_vac, self.normal)
     # flip the orientation of normal if it is not pointing in
     # the right direction.
     if (angle > 90):
         normal_frac = self.lattice.get_fractional_coords(normal)
         normal_frac[2] = -normal_frac[2]
         normal = self.lattice.get_cartesian_coords(normal_frac)
         mov_vec = normal * self.displacement
     # get the index corresponding to the given atomic species in
     # the ligand that will bond with the surface on which the
     # ligand will be adsorbed
     adatom_index = self.get_index(self.adatom_on_lig)
     adsorbed_ligands_coords = []
     # set the ligand coordinates for each adsorption site on
     # the surface
     for sindex in site_indices:
         # align the ligand wrt the site on the surface to which
         # it will be adsorbed
         origin = self.cart_coords[sindex]
         self.ligand.translate_sites(
             list(range(num_atoms)),
             origin - self.ligand[adatom_index].coords)
         # displace the ligand by the given amount in the direction
         # normal to surface
         self.ligand.translate_sites(list(range(num_atoms)), mov_vec)
         # vector pointing from the adatom_on_lig to the
         # ligand center of mass
         vec_adatom_cm = self.ligand.center_of_mass - \
             self.ligand[adatom_index].coords
         # rotate the ligand with respect to a vector that is
         # normal to the vec_adatom_cm and the normal to the surface
         # so that the ligand center of mass is aligned along the
         # outward normal to the surface
         origin = self.ligand[adatom_index].coords
         angle = get_angle(vec_adatom_cm, normal)
         if 1 < abs(angle % 180) < 179:
             # For angles which are not 0 or 180,
             # perform a rotation about the origin along an axis
             # perpendicular to both bonds to align bonds.
             axis = np.cross(vec_adatom_cm, normal)
             op = SymmOp.from_origin_axis_angle(origin, axis, angle)
             self.ligand.apply_operation(op)
         elif abs(abs(angle) - 180) < 1:
             # We have a 180 degree angle.
             # Simply do an inversion about the origin
             for i in range(len(self.ligand)):
                 self.ligand[i] = (self.ligand[i].species_and_occu, origin -
                                   (self.ligand[i].coords - origin))
         # x - y - shifts
         x = self.x_shift
         y = self.y_shift
         rot = self.rot
         if x:
             self.ligand.translate_sites(list(range(num_atoms)),
                                         np.array([x, 0, 0]))
         if y:
             self.ligand.translate_sites(list(range(num_atoms)),
                                         np.array([0, y, 0]))
         if rot:
             self.ligand.apply_operation(
                 SymmOp.from_axis_angle_and_translation(
                     (1, 0, 0),
                     rot[0],
                     angle_in_radians=False,
                     translation_vec=(0, 0, 0)))
             self.ligand.apply_operation(
                 SymmOp.from_axis_angle_and_translation(
                     (0, 1, 0),
                     rot[1],
                     angle_in_radians=False,
                     translation_vec=(0, 0, 0)))
             self.ligand.apply_operation(
                 SymmOp.from_axis_angle_and_translation(
                     (0, 0, 1),
                     rot[2],
                     angle_in_radians=False,
                     translation_vec=(0, 0, 0)))
         # 3d numpy array
         adsorbed_ligands_coords.append(self.ligand.cart_coords)
         # extend the slab structure with the adsorbant atoms
     adsorbed_ligands_coords = np.array(adsorbed_ligands_coords)
     for j in range(len(site_indices)):
         [
             self.append(self.ligand.species_and_occu[i],
                         adsorbed_ligands_coords[j, i, :],
                         coords_are_cartesian=True)
             for i in range(num_atoms)
         ]
示例#11
0
    def get_next_nearest_neighbors(
            self,
            site_index: int,
            inc_inequivalent_site_index: bool = True) -> List[Dict[str, Any]]:
        """Gets information about the bonded next nearest neighbors.

        Args:
            site_index: The site index (zero based).
            inc_inequivalent_site_index: Whether to include the inequivalent
                site indices.

        Returns:
            A list of the next nearest neighbor information. For each next
            nearest neighbor site, returns a :obj:`dict` with the format::

                {'element': el, 'connectivity': con, 'geometry': geom,
                 'angles': angles, 'distance': distance}

            The ``connectivity`` property is the connectivity type to the
            next nearest neighbor, e.g. "face", "corner", or
            "edge". The ``geometry`` property gives the geometry of the
            next nearest neighbor site. See the ``get_site_geometry`` method for
            the format of this data. The ``angles`` property gives the bond
            angles between the site and the next nearest neighbour. Returned as
            a :obj:`list` of :obj:`int`. Multiple bond angles are given when
            the two sites share more than nearest neighbor (e.g. if they are
            face-sharing or edge-sharing). The ``distance`` property gives the
            distance between the site and the next nearest neighbor.
            If ``inc_inequivalent_site_index=True``, the data will have an
            additional key ``'inequiv_index'`` corresponding to the inequivalent
            site index. E.g. if two sites are structurally/symmetrically
            equivalent (depending on the value of ``self.use_symmetry_equivalent_sites`` then
            they will have the same ``inequiv_index``.
        """
        def get_coords(a_site_index, a_site_image):
            return np.asarray(
                self.bonded_structure.structure.lattice.get_cartesian_coords(
                    self.bonded_structure.structure.frac_coords[a_site_index] +
                    a_site_image))

        nn_sites = self.bonded_structure.get_connected_sites(site_index)
        next_nn_sites = [
            site for nn_site in nn_sites
            for site in self.bonded_structure.get_connected_sites(
                nn_site.index, jimage=nn_site.jimage)
        ]

        nn_sites_set = {(site.index, site.jimage) for site in nn_sites}

        seen_nnn_sites = set()
        next_nn_summary = []
        for nnn_site in next_nn_sites:
            if (nnn_site.index == site_index and nnn_site.jimage == (0, 0, 0)
                    or (nnn_site.index, nnn_site.jimage) in seen_nnn_sites):
                # skip the nnn site if it is the original atom of interest
                continue

            seen_nnn_sites.add((nnn_site.index, nnn_site.jimage))

            sites = {(site.index, site.jimage)
                     for site in self.bonded_structure.get_connected_sites(
                         nnn_site.index, jimage=nnn_site.jimage)}
            shared_sites = nn_sites_set.intersection(sites)
            n_shared_atoms = len(shared_sites)

            if n_shared_atoms == 1:
                connectivity = "corner"
            elif n_shared_atoms == 2:
                connectivity = "edge"
            else:
                connectivity = "face"

            site_coords = get_coords(site_index, (0, 0, 0))
            nnn_site_coords = get_coords(nnn_site.index, nnn_site.jimage)
            nn_site_coords = [
                get_coords(nn_site_index, nn_site_image)
                for nn_site_index, nn_site_image in shared_sites
            ]

            # can't just use Structure.get_angles to calculate angles as it
            # doesn't take into account the site image
            angles = [
                get_angle(site_coords - x, nnn_site_coords - x)
                for x in nn_site_coords
            ]

            distance = np.linalg.norm(site_coords - nnn_site_coords)

            geometry = self.get_site_geometry(nnn_site.index)

            summary = {
                "element": str(nnn_site.site.specie),
                "connectivity": connectivity,
                "geometry": geometry,
                "angles": angles,
                "distance": distance,
            }

            if inc_inequivalent_site_index:
                summary["inequiv_index"] = self.equivalent_sites[
                    nnn_site.index]

            next_nn_summary.append(summary)

        return next_nn_summary
示例#12
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)
示例#13
0
def get_angle_between_site_and_neighbors(site, neighbors):
    vec_1 = site.coords - neighbors[1].site.coords
    vec_2 = site.coords - neighbors[0].site.coords
    return get_angle(vec_1, vec_2)
示例#14
0
 def test_get_angle(self):
     v1 = (1, 0, 0)
     v2 = (1, 1, 1)
     self.assertAlmostEqual(coord.get_angle(v1, v2), 54.7356103172)
     self.assertAlmostEqual(coord.get_angle(v1, v2, units="radians"),
                            0.9553166181245092)