示例#1
0
    def test_get_points_in_sphere_pbc(self):
        latt = Lattice.cubic(1)
        pts = []
        for a, b, c in itertools.product(xrange(10), xrange(10), xrange(10)):
            pts.append([a / 10, b / 10, c / 10])

        self.assertEqual(
            len(get_points_in_sphere_pbc(latt, pts, [0, 0, 0], 0.1)), 7)
        self.assertEqual(
            len(get_points_in_sphere_pbc(latt, pts, [0.5, 0.5, 0.5], 0.5)),
            515)
示例#2
0
    def test_get_points_in_sphere_pbc(self):
        latt = Lattice.cubic(1)
        pts = []
        for a, b, c in itertools.product(xrange(10), xrange(10), xrange(10)):
            pts.append([a / 10, b / 10, c / 10])

        self.assertEqual(len(get_points_in_sphere_pbc(latt, pts, [0, 0, 0],
                                                      0.1)), 7)
        self.assertEqual(len(get_points_in_sphere_pbc(latt, pts,
                                                      [0.5, 0.5, 0.5],
                                                      0.5)), 515)
示例#3
0
    def find_mapping(self, other_lattice, ltol=1e-5, atol=1):
        """
        Finds a mapping between current lattice and another lattice. There
        are an infinite number of choices of basis vectors for two entirely
        equivalent lattices. This method returns a mapping that maps
        other_lattice to this lattice.

        Args:
            other_lattice:
                Another lattice that is equivalent to this one.
            ltol:
                Tolerance for matching lengths. Defaults to 1e-5.
            atol:
                Tolerance for matching angles. Defaults to 1.

        Returns:
            (aligned_lattice, rotation_matrix, scale_matrix) if a mapping is
            found. aligned_lattice is a rotated version of other_lattice that
            has the same lattice parameters, but which is aligned in the
            coordinate system of this lattice so that translational points
            match up in 3D. rotation_matrix is the rotation that has to be
            applied to other_lattice to obtain aligned_lattice, i.e.,
            aligned_matrix = rotation_matrix * other_lattice.
            Finally, scale_matrix is the integer matrix that expresses
            aligned_matrix as a linear combination of this
            lattice, i.e., aligned_matrix = scale_matrix * self

            None is returned if no matches are found.
        """
        (lengths, angles) = other_lattice.lengths_and_angles
        (alpha, beta, gamma) = angles

        points = get_points_in_sphere_pbc(self, [[0, 0, 0]], [0, 0, 0],
                                          max(lengths) + 0.1)
        all_frac = [p[0] for p in points]
        dist = [p[1] for p in points]
        cart = self.get_cartesian_coords(all_frac)
        data = zip(cart, dist)
        candidates = [
            filter(lambda d: abs(d[1] - l) < ltol, data) for l in lengths
        ]

        def get_angle(v1, v2):
            x = dot(v1[0], v2[0]) / v1[1] / v2[1]
            x = min(1, x)
            x = max(-1, x)
            angle = np.arccos(x) * 180. / pi
            return angle

        for m1, m2, m3 in itertools.product(*candidates):
            if abs(get_angle(m1, m2) - gamma) < atol and\
                    abs(get_angle(m2, m3) - alpha) < atol and\
                    abs(get_angle(m1, m3) - beta) < atol:
                aligned_m = np.array([m1[0], m2[0], m3[0]])
                rotation_matrix = np.linalg.solve(other_lattice.matrix.T,
                                                  aligned_m.T).T
                scale_matrix = np.linalg.solve(aligned_m.T, self._matrix.T).T
                return Lattice(aligned_m), rotation_matrix, scale_matrix

        return None
示例#4
0
    def find_mapping(self, other_lattice, ltol=1e-5, atol=1):
        """
        Finds a mapping between current lattice and another lattice. There
        are an infinite number of choices of basis vectors for two entirely
        equivalent lattices. This method returns a mapping that maps
        other_lattice to this lattice.

        Args:
            other_lattice:
                Another lattice that is equivalent to this one.
            ltol:
                Tolerance for matching lengths. Defaults to 1e-5.
            atol:
                Tolerance for matching angles. Defaults to 1.

        Returns:
            (aligned_lattice, rotation_matrix, scale_matrix) if a mapping is
            found. aligned_lattice is a rotated version of other_lattice that
            has the same lattice parameters, but which is aligned in the
            coordinate system of this lattice so that translational points
            match up in 3D. rotation_matrix is the rotation that has to be
            applied to other_lattice to obtain aligned_lattice, i.e.,
            aligned_matrix = rotation_matrix * other_lattice.
            Finally, scale_matrix is the integer matrix that expresses
            aligned_matrix as a linear combination of this
            lattice, i.e., aligned_matrix = scale_matrix * self

            None is returned if no matches are found.
        """
        (lengths, angles) = other_lattice.lengths_and_angles
        (alpha, beta, gamma) = angles

        points = get_points_in_sphere_pbc(self, [[0, 0, 0]], [0, 0, 0],
                                          max(lengths) + 0.1)
        all_frac = [p[0] for p in points]
        dist = [p[1] for p in points]
        cart = self.get_cartesian_coords(all_frac)
        data = zip(cart, dist)
        candidates = [filter(lambda d: abs(d[1] - l) < ltol, data)
                      for l in lengths]

        def get_angle(v1, v2):
            x = dot(v1[0], v2[0]) / v1[1] / v2[1]
            x = min(1, x)
            x = max(-1, x)
            angle = np.arccos(x) * 180. / pi
            angle = np.around(angle, 9)
            return angle

        for m1, m2, m3 in itertools.product(*candidates):
            if abs(get_angle(m1, m2) - gamma) < atol and\
                    abs(get_angle(m2, m3) - alpha) < atol and\
                    abs(get_angle(m1, m3) - beta) < atol:
                aligned_m = np.array([m1[0], m2[0], m3[0]])
                rotation_matrix = np.linalg.solve(other_lattice.matrix.T,
                                                  aligned_m.T).T
                scale_matrix = np.linalg.solve(aligned_m.T, self._matrix.T).T
                return Lattice(aligned_m), rotation_matrix, scale_matrix

        return None
示例#5
0
    def _calc_recip(self):
        """
        Perform the reciprocal space summation. Calculates the quantity
        E_recip = 1/(2PiV) sum_{G < Gmax} exp(-(G.G/4/eta))/(G.G) S(G)S(-G)
        where
        S(G) = sum_{k=1,N} q_k exp(-i G.r_k)
        S(G)S(-G) = |S(G)|**2

        This method is heavily vectorized to utilize numpy's C backend for
        speed.
        """
        numsites = self._s.num_sites
        prefactor = 2 * pi / self._vol
        erecip = np.zeros((numsites, numsites))
        forces = np.zeros((numsites, 3))
        coords = self._coords
        rcp_latt = self._s.lattice.reciprocal_lattice
        recip_nn = get_points_in_sphere_pbc(rcp_latt, [[0, 0, 0]], [0, 0, 0],
                                            self._gmax)

        frac_to_cart = rcp_latt.get_cartesian_coords

        oxistates = np.array(self._oxi_states)
        #create array where q_2[i,j] is qi * qj
        qiqj = oxistates[None, :] * oxistates[:, None]

        for (fcoords, dist, i) in recip_nn:
            if dist == 0:
                continue
            gvect = frac_to_cart(fcoords)
            gsquare = np.linalg.norm(gvect) ** 2

            expval = exp(-1.0 * gsquare / (4.0 * self._eta))

            gvectdot = np.sum(gvect[None, :] * coords, 1)

            #calculate the structure factor
            sreal = np.sum(oxistates * np.cos(gvectdot))
            simag = np.sum(oxistates * np.sin(gvectdot))

            #create array where exparg[i,j] is gvectdot[i] - gvectdot[j]
            exparg = gvectdot[None, :] - gvectdot[:, None]

            #uses the identity sin(x)+cos(x) = 2**0.5 sin(x + pi/4)
            sfactor = qiqj * np.sin(exparg + pi / 4) * 2 ** 0.5

            erecip += expval / gsquare * sfactor
            pref = 2 * expval / gsquare * oxistates
            factor = prefactor * pref * \
                (sreal * np.sin(gvectdot)
                 - simag * np.cos(gvectdot)) * EwaldSummation.CONV_FACT

            forces += factor[:, None] * gvect[None, :]

        return erecip * prefactor * EwaldSummation.CONV_FACT, forces
示例#6
0
    def _calc_recip(self):
        """
        Perform the reciprocal space summation. Calculates the quantity
        E_recip = 1/(2PiV) sum_{G < Gmax} exp(-(G.G/4/eta))/(G.G) S(G)S(-G)
        where
        S(G) = sum_{k=1,N} q_k exp(-i G.r_k)
        S(G)S(-G) = |S(G)|**2

        This method is heavily vectorized to utilize numpy's C backend for
        speed.
        """
        numsites = self._s.num_sites
        prefactor = 2 * pi / self._vol
        erecip = np.zeros((numsites, numsites))
        forces = np.zeros((numsites, 3))
        coords = self._coords
        rcp_latt = self._s.lattice.reciprocal_lattice
        recip_nn = get_points_in_sphere_pbc(rcp_latt, [[0, 0, 0]], [0, 0, 0],
                                            self._gmax)

        frac_to_cart = rcp_latt.get_cartesian_coords

        oxistates = np.array(self._oxi_states)
        #create array where q_2[i,j] is qi * qj
        qiqj = oxistates[None, :] * oxistates[:, None]

        for (fcoords, dist, i) in recip_nn:
            if dist == 0:
                continue
            gvect = frac_to_cart(fcoords)
            gsquare = np.linalg.norm(gvect)**2

            expval = exp(-1.0 * gsquare / (4.0 * self._eta))

            gvectdot = np.sum(gvect[None, :] * coords, 1)

            #calculate the structure factor
            sreal = np.sum(oxistates * np.cos(gvectdot))
            simag = np.sum(oxistates * np.sin(gvectdot))

            #create array where exparg[i,j] is gvectdot[i] - gvectdot[j]
            exparg = gvectdot[None, :] - gvectdot[:, None]

            #uses the identity sin(x)+cos(x) = 2**0.5 sin(x + pi/4)
            sfactor = qiqj * np.sin(exparg + pi / 4) * 2**0.5

            erecip += expval / gsquare * sfactor
            pref = 2 * expval / gsquare * oxistates
            factor = prefactor * pref * \
                (sreal * np.sin(gvectdot)
                 - simag * np.cos(gvectdot)) * EwaldSummation.CONV_FACT

            forces += factor[:, None] * gvect[None, :]

        return erecip * prefactor * EwaldSummation.CONV_FACT, forces
示例#7
0
    def _get_lattices(self, s1, s2, vol_tol):
        s1_lengths, s1_angles = s1.lattice.lengths_and_angles
        all_nn = get_points_in_sphere_pbc(s2.lattice, [[0, 0, 0]], [0, 0, 0],
                                          (1 + self.ltol) *
                                          max(s1_lengths))[:, [0, 1]]

        nv = []
        for l in s1_lengths:
            nvi = all_nn[np.where((all_nn[:, 1] < (1 + self.ltol) * l)
                                  & (all_nn[:, 1] > (1 - self.ltol) * l))][:,
                                                                           0]
            if not len(nvi):
                return
            nvi = [np.array(site) for site in nvi]
            nvi = np.dot(nvi, s2.lattice.matrix)
            nv.append(nvi)
            #The vectors are broadcast into a 5-D array containing
        #all permutations of the entries in nv[0], nv[1], nv[2]
        #produces the same result as three nested loops over the
        #same variables and calculating determinants individually
        bfl = (np.array(nv[0])[None, None, :, None, :] *
               np.array([1, 0, 0])[None, None, None, :, None] +
               np.array(nv[1])[None, :, None, None, :] *
               np.array([0, 1, 0])[None, None, None, :, None] +
               np.array(nv[2])[:, None, None, None, :] *
               np.array([0, 0, 1])[None, None, None, :, None])

        #Compute volume of each array
        vol = np.sum(
            bfl[:, :, :, 0, :] *
            np.cross(bfl[:, :, :, 1, :], bfl[:, :, :, 2, :]), 3)
        #Find valid lattices
        valid = np.where(abs(vol) >= vol_tol)
        if not len(valid[0]):
            return
            #loop over valid lattices to compute the angles for each
        lengths = np.sum(bfl[valid]**2, axis=2)**0.5
        angles = np.zeros((len(bfl[valid]), 3), float)
        for i in xrange(3):
            j = (i + 1) % 3
            k = (i + 2) % 3
            angles[:, i] = \
                np.sum(bfl[valid][:, j, :] * bfl[valid][:, k, :], 1) \
                / (lengths[:, j] * lengths[:, k])
        angles = np.arccos(angles) * 180. / np.pi
        #Check angles are within tolerance
        valid_angles = np.where(
            np.all(np.abs(angles - s1_angles) < self.angle_tol, axis=1))
        if len(valid_angles[0]) == 0:
            return
            #yield valid lattices
        for lat in bfl[valid][valid_angles]:
            nl = Lattice(lat)
            yield nl
示例#8
0
    def _get_lattices(self, s1, s2, vol_tol):
        s1_lengths, s1_angles = s1.lattice.lengths_and_angles
        all_nn = get_points_in_sphere_pbc(
            s2.lattice, [[0, 0, 0]], [0, 0, 0],
            (1 + self.ltol) * max(s1_lengths))[:, [0, 1]]

        nv = []
        for l in s1_lengths:
            nvi = all_nn[np.where((all_nn[:, 1] < (1 + self.ltol) * l)
                                  &
                                  (all_nn[:, 1] > (1 - self.ltol) * l))][:, 0]
            if not len(nvi):
                return
            nvi = [np.array(site) for site in nvi]
            nvi = np.dot(nvi, s2.lattice.matrix)
            nv.append(nvi)
            #The vectors are broadcast into a 5-D array containing
        #all permutations of the entries in nv[0], nv[1], nv[2]
        #produces the same result as three nested loops over the
        #same variables and calculating determinants individually
        bfl = (np.array(nv[0])[None, None, :, None, :] *
               np.array([1, 0, 0])[None, None, None, :, None] +
               np.array(nv[1])[None, :, None, None, :] *
               np.array([0, 1, 0])[None, None, None, :, None] +
               np.array(nv[2])[:, None, None, None, :] *
               np.array([0, 0, 1])[None, None, None, :, None])

        #Compute volume of each array
        vol = np.sum(bfl[:, :, :, 0, :] * np.cross(bfl[:, :, :, 1, :],
                                                   bfl[:, :, :, 2, :]), 3)
        #Find valid lattices
        valid = np.where(abs(vol) >= vol_tol)
        if not len(valid[0]):
            return
            #loop over valid lattices to compute the angles for each
        lengths = np.sum(bfl[valid] ** 2, axis=2) ** 0.5
        angles = np.zeros((len(bfl[valid]), 3), float)
        for i in xrange(3):
            j = (i + 1) % 3
            k = (i + 2) % 3
            angles[:, i] = \
                np.sum(bfl[valid][:, j, :] * bfl[valid][:, k, :], 1) \
                / (lengths[:, j] * lengths[:, k])
        angles = np.arccos(angles) * 180. / np.pi
        #Check angles are within tolerance
        valid_angles = np.where(np.all(np.abs(angles - s1_angles) <
                                       self.angle_tol, axis=1))
        if len(valid_angles[0]) == 0:
            return
            #yield valid lattices
        for lat in bfl[valid][valid_angles]:
            nl = Lattice(lat)
            yield nl
示例#9
0
 def to_unit_cell(self, tolerance=0.1):
     """
     Returns all the sites to their position inside the unit cell.
     If there is a site within the tolerance already there, the site is
     deleted instead of moved.
     """
     new_sites = []
     for site in self._sites:
         if not new_sites:
             new_sites.append(site)
             frac_coords = np.array([site.frac_coords])
             continue
         if len(get_points_in_sphere_pbc(self._lattice, frac_coords,
                                         site.coords, tolerance)):
             continue
         frac_coords = np.append(frac_coords, [site.frac_coords % 1],
                                 axis=0)
         new_sites.append(site.to_unit_cell)
     self._sites = new_sites
示例#10
0
 def to_unit_cell(self, tolerance=0.1):
     """
     Returns all the sites to their position inside the unit cell.
     If there is a site within the tolerance already there, the site is
     deleted instead of moved.
     """
     new_sites = []
     for site in self._sites:
         if not new_sites:
             new_sites.append(site)
             frac_coords = np.array([site.frac_coords])
             continue
         if len(
                 get_points_in_sphere_pbc(self._lattice, frac_coords,
                                          site.coords, tolerance)):
             continue
         frac_coords = np.append(frac_coords, [site.frac_coords % 1],
                                 axis=0)
         new_sites.append(site.to_unit_cell)
     self._sites = new_sites
示例#11
0
    def get_sites_in_sphere(self, pt, r, include_index=False):
        """
        Find all sites within a sphere from the point. This includes sites
        in other periodic images.

        Algorithm:

        1. place sphere of radius r in crystal and determine minimum supercell
           (parallelpiped) which would contain a sphere of radius r. for this
           we need the projection of a_1 on a unit vector perpendicular
           to a_2 & a_3 (i.e. the unit vector in the direction b_1) to
           determine how many a_1"s it will take to contain the sphere.

           Nxmax = r * length_of_b_1 / (2 Pi)

        2. keep points falling within r.

        Args:
            pt:
                cartesian coordinates of center of sphere.
            r:
                radius of sphere.
            include_index:
                boolean that determines whether the non-supercell site index
                is included in the returned data

        Returns:
            [(site, dist) ...] since most of the time, subsequent processing
            requires the distance.
        """
        site_fcoords = np.mod(self.frac_coords, 1)
        neighbors = []
        for fcoord, dist, i in get_points_in_sphere_pbc(self._lattice,
                                                        site_fcoords, pt, r):
            nnsite = PeriodicSite(self[i].species_and_occu,
                                  fcoord, self._lattice,
                                  properties=self[i].properties)
            neighbors.append((nnsite, dist) if not include_index
                             else (nnsite, dist, i))
        return neighbors
示例#12
0
    def _get_lattices(self, target_s, s, supercell_size=1):
        """
        Yields lattices for s with lengths and angles close to the
        lattice of target_s. If supercell_size is specified, the
        returned lattice will have that number of primitive cells
        in it

        Args:
            s, target_s: Structure objects
        """
        t_l, t_a = target_s.lattice.lengths_and_angles
        r = (1 + self.ltol) * max(t_l)
        fpts, dists, i = get_points_in_sphere_pbc(
            lattice=s.lattice, frac_points=[[0, 0, 0]], center=[0, 0, 0],
            r=r).T
        #get possible vectors for a, b, and c
        new_v = []
        for l in t_l:
            max_r = (1 + self.ltol) * l
            min_r = (1 - self.ltol) * l
            vi = fpts[np.where((dists < max_r) & (dists > min_r))]
            if len(vi) == 0:
                return
            cart_vi = np.dot(np.array([i for i in vi]), s.lattice.matrix)
            new_v.append(cart_vi)

        #The vectors are broadcast into a 5-D array containing
        #all permutations of the entries in new_v[0], new_v[1], new_v[2]
        #Produces the same result as three nested loops over the
        #same variables and calculating determinants individually
        bfl = (np.array(new_v[0])[None, None, :, None, :] *
               np.array([1, 0, 0])[None, None, None, :, None] +
               np.array(new_v[1])[None, :, None, None, :] *
               np.array([0, 1, 0])[None, None, None, :, None] +
               np.array(new_v[2])[:, None, None, None, :] *
               np.array([0, 0, 1])[None, None, None, :, None])

        #Compute volume of each lattice
        vol = np.abs(np.sum(bfl[:, :, :, 0, :] *
                            np.cross(bfl[:, :, :, 1, :],
                                     bfl[:, :, :, 2, :]), 3))
        #valid lattices must not change volume
        min_vol = s.volume * 0.999 * supercell_size
        max_vol = s.volume * 1.001 * supercell_size
        bfl = bfl[np.where((vol > min_vol) & (vol < max_vol))]
        if len(bfl) == 0:
            return

        #compute angles
        lengths = np.sum(bfl ** 2, axis=2) ** 0.5
        angles = np.zeros((len(bfl), 3), float)
        for i in xrange(3):
            j = (i + 1) % 3
            k = (i + 2) % 3
            angles[:, i] = \
                np.sum(bfl[:, j, :] * bfl[:, k, :], 1) \
                / (lengths[:, j] * lengths[:, k])
        angles = np.arccos(angles) * 180. / np.pi
        #Check angles are within tolerance
        valid_angles = np.where(np.all(np.abs(angles - t_a) <
                                       self.angle_tol, axis=1))
        for lat in bfl[valid_angles]:
            nl = Lattice(lat)
            yield nl
示例#13
0
    def _get_lattices(self, target_s, s, supercell_size=1):
        """
        Yields lattices for s with lengths and angles close to the
        lattice of target_s. If supercell_size is specified, the
        returned lattice will have that number of primitive cells
        in it

        Args:
            s, target_s: Structure objects
        """
        t_l, t_a = target_s.lattice.lengths_and_angles
        r = (1 + self.ltol) * max(t_l)
        fpts, dists, i = get_points_in_sphere_pbc(lattice=s.lattice,
                                                  frac_points=[[0, 0, 0]],
                                                  center=[0, 0, 0],
                                                  r=r).T
        #get possible vectors for a, b, and c
        new_v = []
        for l in t_l:
            max_r = (1 + self.ltol) * l
            min_r = (1 - self.ltol) * l
            vi = fpts[np.where((dists < max_r) & (dists > min_r))]
            if len(vi) == 0:
                return
            cart_vi = np.dot(np.array([i for i in vi]), s.lattice.matrix)
            new_v.append(cart_vi)

        #The vectors are broadcast into a 5-D array containing
        #all permutations of the entries in new_v[0], new_v[1], new_v[2]
        #Produces the same result as three nested loops over the
        #same variables and calculating determinants individually
        bfl = (np.array(new_v[0])[None, None, :, None, :] *
               np.array([1, 0, 0])[None, None, None, :, None] +
               np.array(new_v[1])[None, :, None, None, :] *
               np.array([0, 1, 0])[None, None, None, :, None] +
               np.array(new_v[2])[:, None, None, None, :] *
               np.array([0, 0, 1])[None, None, None, :, None])

        #Compute volume of each lattice
        vol = np.abs(
            np.sum(
                bfl[:, :, :, 0, :] *
                np.cross(bfl[:, :, :, 1, :], bfl[:, :, :, 2, :]), 3))
        #valid lattices must not change volume
        min_vol = s.volume * 0.999 * supercell_size
        max_vol = s.volume * 1.001 * supercell_size
        bfl = bfl[np.where((vol > min_vol) & (vol < max_vol))]
        if len(bfl) == 0:
            return

        #compute angles
        lengths = np.sum(bfl**2, axis=2)**0.5
        angles = np.zeros((len(bfl), 3), float)
        for i in xrange(3):
            j = (i + 1) % 3
            k = (i + 2) % 3
            angles[:, i] = \
                np.sum(bfl[:, j, :] * bfl[:, k, :], 1) \
                / (lengths[:, j] * lengths[:, k])
        angles = np.arccos(angles) * 180. / np.pi
        #Check angles are within tolerance
        valid_angles = np.where(
            np.all(np.abs(angles - t_a) < self.angle_tol, axis=1))
        for lat in bfl[valid_angles]:
            nl = Lattice(lat)
            yield nl