Ejemplo n.º 1
0
    def test_pbc_shortest_vectors(self):
        fcoords = np.array([[0.3, 0.3, 0.5],
                            [0.1, 0.1, 0.3],
                            [0.9, 0.9, 0.8],
                            [0.1, 0.0, 0.5],
                            [0.9, 0.7, 0.0]])
        lattice = Lattice.from_lengths_and_angles([8, 8, 4],
                                                  [90, 76, 58])
        expected = np.array([[0.000, 3.015, 4.072, 3.519, 3.245],
                             [3.015, 0.000, 3.207, 1.131, 4.453],
                             [4.072, 3.207, 0.000, 2.251, 1.788],
                             [3.519, 1.131, 2.251, 0.000, 3.852]])

        vectors = pbc_shortest_vectors(lattice, fcoords[:-1], fcoords)
        dists = np.sum(vectors**2, axis = -1)**0.5
        self.assertArrayAlmostEqual(dists, expected, 3)

        #now try with small loop threshold
        from pymatgen.util import coord_utils
        prev_threshold = coord_utils.LOOP_THRESHOLD
        coord_utils.LOOP_THRESHOLD = 0

        vectors = pbc_shortest_vectors(lattice, fcoords[:-1], fcoords)
        dists = np.sum(vectors**2, axis = -1)**0.5
        self.assertArrayAlmostEqual(dists, expected, 3)

        coord_utils.LOOP_THRESHOLD = prev_threshold
Ejemplo n.º 2
0
    def _cart_dists(self, s1, s2, avg_lattice, mask, normalization):
        """
        Finds a matching in cartesian space. Finds an additional
        fractional translation vector to minimize RMS distance

        Args:
            s1, s2: numpy arrays of fractional coordinates. len(s1) >= len(s2)
            avg_lattice: Lattice on which to calculate distances
            mask: numpy array of booleans. mask[i, j] = True indicates
                that s2[i] cannot be matched to s1[j]
            normalization (float): inverse normalization length

        Returns:
            Distances from s2 to s1, normalized by (V/Natom) ^ 1/3
            Fractional translation vector to apply to s2.
            Mapping from s1 to s2, i.e. with numpy slicing, s1[mapping] => s2
        """
        if len(s2) > len(s1):
            raise ValueError("s1 must be larger than s2")
        if mask.shape != (len(s2), len(s1)):
            raise ValueError("mask has incorrect shape")

        mask_val = 1e10 * self.stol / normalization
        #vectors are from s2 to s1
        vecs = pbc_shortest_vectors(avg_lattice, s2, s1)
        vecs[mask] = mask_val
        d_2 = np.sum(vecs**2, axis=-1)
        lin = LinearAssignment(d_2)
        s = lin.solution
        short_vecs = vecs[np.arange(len(s)), s]
        translation = np.average(short_vecs, axis=0)
        f_translation = avg_lattice.get_fractional_coords(translation)
        new_d2 = np.sum((short_vecs - translation)**2, axis=-1)

        return new_d2**0.5 * normalization, f_translation, s
Ejemplo n.º 3
0
    def get_distance_and_image(self, frac_coords1, frac_coords2, jimage=None):
        """
        Gets distance between two frac_coords assuming periodic boundary
        conditions. If the index jimage is not specified it selects the j
        image nearest to the i atom and returns the distance and jimage
        indices in terms of lattice vector translations. If the index jimage
        is specified it returns the distance between the frac_coords1 and
        the specified jimage of frac_coords2, and the given jimage is also
        returned.

        Args:
            fcoords1 (3x1 array): Reference fcoords to get distance from.
            fcoords2 (3x1 array): fcoords to get distance from.
            jimage (3x1 array): Specific periodic image in terms of
                lattice translations, e.g., [1,0,0] implies to take periodic
                image that is one a-lattice vector away. If jimage is None,
                the image that is nearest to the site is found.

        Returns:
            (distance, jimage): distance and periodic lattice translations
            of the other site for which the distance applies. This means that
            the distance between frac_coords1 and (jimage + frac_coords2) is
            equal to distance.
        """
        if jimage is None:
            v, d2 = pbc_shortest_vectors(self, frac_coords1, frac_coords2,
                                         return_d2=True)
            fc = self.get_fractional_coords(v[0][0]) + frac_coords1 - \
                 frac_coords2
            fc = np.array(np.round(fc), dtype=np.int)
            return (np.sqrt(d2[0, 0]), fc)

        mapped_vec = self.get_cartesian_coords(jimage + frac_coords2
                                               - frac_coords1)
        return np.linalg.norm(mapped_vec), jimage
Ejemplo n.º 4
0
    def _cart_dists(self, s1, s2, avg_lattice, mask, normalization):
        """
        Finds a matching in cartesian space. Finds an additional
        fractional translation vector to minimize RMS distance

        Args:
            s1, s2: numpy arrays of fractional coordinates. len(s1) >= len(s2)
            avg_lattice: Lattice on which to calculate distances
            mask: numpy array of booleans. mask[i, j] = True indicates
                that s2[i] cannot be matched to s1[j]
            normalization (float): inverse normalization length

        Returns:
            Distances from s2 to s1, normalized by (V/Natom) ^ 1/3
            Fractional translation vector to apply to s2.
            Mapping from s1 to s2, i.e. with numpy slicing, s1[mapping] => s2
        """
        if len(s2) > len(s1):
            raise ValueError("s1 must be larger than s2")
        if mask.shape != (len(s2), len(s1)):
            raise ValueError("mask has incorrect shape")

        mask_val = 1e10 * self.stol / normalization
        #vectors are from s2 to s1
        vecs = pbc_shortest_vectors(avg_lattice, s2, s1)
        vecs[mask] = mask_val
        d_2 = np.sum(vecs ** 2, axis=-1)
        lin = LinearAssignment(d_2)
        s = lin.solution
        short_vecs = vecs[np.arange(len(s)), s]
        translation = np.average(short_vecs, axis=0)
        f_translation = avg_lattice.get_fractional_coords(translation)
        new_d2 = np.sum((short_vecs - translation) ** 2, axis=-1)

        return new_d2 ** 0.5 * normalization, f_translation, s
Ejemplo n.º 5
0
    def get_distance_and_image(self, frac_coords1, frac_coords2, jimage=None):
        """
        Gets distance between two frac_coords assuming periodic boundary
        conditions. If the index jimage is not specified it selects the j
        image nearest to the i atom and returns the distance and jimage
        indices in terms of lattice vector translations. If the index jimage
        is specified it returns the distance between the frac_coords1 and
        the specified jimage of frac_coords2, and the given jimage is also
        returned.

        Args:
            fcoords1 (3x1 array): Reference fcoords to get distance from.
            fcoords2 (3x1 array): fcoords to get distance from.
            jimage (3x1 array): Specific periodic image in terms of
                lattice translations, e.g., [1,0,0] implies to take periodic
                image that is one a-lattice vector away. If jimage == None,
                the image that is nearest to the site is found.

        Returns:
            (distance, jimage): distance and periodic lattice translations
            of the other site for which the distance applies. This means that
            the distance between frac_coords1 and (jimage + frac_coords2) is
            equal to distance.
        """
        if jimage is None:
            v, d2 = pbc_shortest_vectors(self, frac_coords1, frac_coords2,
                                         return_d2=True)
            fc = self.get_fractional_coords(v[0][0]) + frac_coords1 - \
                 frac_coords2
            fc = np.array(np.round(fc), dtype=np.int)
            return (np.sqrt(d2[0, 0]), fc)

        mapped_vec = self.get_cartesian_coords(jimage + frac_coords2
                                               - frac_coords1)
        return np.linalg.norm(mapped_vec), jimage
Ejemplo n.º 6
0
    def _cart_dists(self, s1, s2, l1, l2, mask):
        """
        Finds the cartesian distances normalized by (V/Natom) ^ 1/3
        between s1 and s2 on the average lattice of l1 and l2
        s1 and s2 are lists of fractional coordinates. Minimizes the
        RMS distance of the matching with an additional translation
        (but doesn't change the mapping)
        returns distances, fractional_translation vector
        """
        #create the average lattice
        avg_params = (np.array(l1.lengths_and_angles) +
                      np.array(l2.lengths_and_angles)) / 2
        avg_lattice = Lattice.from_lengths_and_angles(*avg_params)
        norm_length = (avg_lattice.volume / len(s1)) ** (1 / 3)
        mask_val = 1e20 * norm_length * self.stol

        all_d_2 = np.zeros([len(s1), len(s1)])
        vec_matrix = np.zeros([len(s1), len(s1), 3])

        vecs = pbc_shortest_vectors(avg_lattice, s2, s1)
        vec_matrix[:len(s2)] = vecs
        vec_matrix[mask] = mask_val
        d_2 = (np.sum(vecs ** 2, axis=-1))
        all_d_2[:len(s2)] = d_2
        all_d_2[mask] = mask_val

        lin = LinearAssignment(all_d_2)
        inds = np.arange(len(s2))
        shortest_vecs = vec_matrix[inds, lin.solution[:len(s2)], :]
        translation = np.average(shortest_vecs, axis=0)
        f_translation = avg_lattice.get_fractional_coords(translation)
        shortest_distances = np.sum((shortest_vecs - translation) ** 2,
                                    -1) ** 0.5
        return shortest_distances / norm_length, f_translation
Ejemplo n.º 7
0
 def test_pbc_shortest_vectors(self):
     fcoords = np.array([[0.3, 0.3, 0.5], [0.1, 0.1, 0.3], [0.9, 0.9, 0.8],
                         [0.1, 0.0, 0.5], [0.9, 0.7, 0.0]])
     lattice = Lattice.from_lengths_and_angles([8, 8, 4], [90, 76, 58])
     expected = np.array([[0.000, 3.015, 4.072, 3.519, 3.245],
                          [3.015, 0.000, 3.207, 1.131, 4.453],
                          [4.072, 3.207, 0.000, 2.251, 1.788],
                          [3.519, 1.131, 2.251, 0.000, 3.852]])
     vectors = pbc_shortest_vectors(lattice, fcoords[:-1], fcoords)
     dists = np.sum(vectors**2, axis=-1)**0.5
     self.assertArrayAlmostEqual(dists, expected, 3)
Ejemplo n.º 8
0
 def test_pbc_shortest_vectors(self):
     fcoords = np.array([[0.3, 0.3, 0.5],
                         [0.1, 0.1, 0.3],
                         [0.9, 0.9, 0.8],
                         [0.1, 0.0, 0.5],
                         [0.9, 0.7, 0.0]])
     lattice = Lattice.from_lengths_and_angles([8, 8, 4],
                                               [90, 76, 58])
     expected = np.array([[0.000, 3.015, 4.072, 3.519, 3.245],
                          [3.015, 0.000, 3.207, 1.131, 4.453],
                          [4.072, 3.207, 0.000, 2.251, 1.788],
                          [3.519, 1.131, 2.251, 0.000, 3.852]])
     vectors = pbc_shortest_vectors(lattice, fcoords[:-1], fcoords)
     dists = np.sum(vectors**2, axis = -1)**0.5
     self.assertArrayAlmostEqual(dists, expected, 3)
Ejemplo n.º 9
0
    def _cart_dists(self, s1, s2, l1, l2, mask):
        """
        Finds the cartesian distances normalized by (V/Natom) ^ 1/3
        between two structures on the average lattice of l1 and l2
        s_superset and s_subset are lists of fractional coordinates.
        Minimizes the RMS distance of the matching with an additional
        translation (but doesn't change the mapping)
        returns distances, fractional_translation vector
        """
        #ensure that we always calculate distances from the subset
        #to the superset
        if len(s1) > len(s2):
            s_superset, s_subset, mult = s1, s2, 1
        else:
            s_superset, s_subset, mult = s2, s1, -1
            mask = mask.T
        #create the average lattice
        avg_params = (np.array(l1.lengths_and_angles) +
                      np.array(l2.lengths_and_angles)) / 2
        avg_lattice = Lattice.from_lengths_and_angles(*avg_params)
        norm_length = (avg_lattice.volume / len(s_superset)) ** (1 / 3)
        mask_val = 1e20 * norm_length * self.stol

        all_d_2 = np.zeros([len(s_superset), len(s_superset)])
        vec_matrix = np.zeros([len(s_superset), len(s_superset), 3])

        #vectors from subset to superset
        #1st index subset, 2nd index superset
        vecs = pbc_shortest_vectors(avg_lattice, s_subset, s_superset)
        vec_matrix[:len(s_subset), :len(s_superset)] = vecs
        vec_matrix[mask] = mask_val
        d_2 = (np.sum(vecs ** 2, axis=-1))
        all_d_2[:len(s_subset), :len(s_superset)] = d_2
        all_d_2[mask] = mask_val
        lin = LinearAssignment(all_d_2)
        inds = np.arange(len(s_subset))
        #shortest vectors from the subset to the superset
        shortest_vecs = vec_matrix[inds, lin.solution[:len(s_subset)], :]
        translation = np.average(shortest_vecs, axis=0)
        f_translation = avg_lattice.get_fractional_coords(translation)
        shortest_distances = np.sum((shortest_vecs - translation) ** 2,
                                    -1) ** 0.5
        return shortest_distances / norm_length, f_translation * mult
Ejemplo n.º 10
0
    def _cmp_cartesian_struct(self, s1, s2, l1, l2):
        """
        Once a fit is found, a rms minimizing fit is done to
        ensure the fit is correct. To do this,

        1) The structures are placed into an average lattice
        2) All sites are shifted by the mean
            displacement vector between matched sites.
        3) calculate distances
        4) return rms distance normalized by (V/Natom) ^ 1/3
            and the maximum distance found
        """
        nsites = sum(map(len, s1))

        avg_params = (np.array(l1.lengths_and_angles) +
                      np.array(l2.lengths_and_angles)) / 2

        avg_lattice = Lattice.from_lengths_and_angles(avg_params[0],
                                                      avg_params[1])
        dist = np.zeros([nsites, nsites]) + 100 * nsites
        vec_matrix = np.zeros([nsites, nsites, 3])
        i = 0
        for s1_coords, s2_coords in zip(s1, s2):
            j = len(s1_coords)
            vecs = pbc_shortest_vectors(avg_lattice, s1_coords, s2_coords)
            distances = (np.sum(vecs ** 2, axis=-1)) ** 0.5
            dist[i: i + j, i: i + j] = distances
            vec_matrix[i: i + j, i: i + j] = vecs
            i += j
        lin = LinearAssignment(dist)
        inds = np.arange(nsites)

        shortest_vecs = vec_matrix[inds, lin.solution, :]
        shortest_vec_square = np.sum(
            (shortest_vecs - np.average(shortest_vecs, axis=0)) ** 2, -1)

        norm_length = (avg_lattice.volume / nsites) ** (1 / 3)

        rms = np.average(shortest_vec_square) ** 0.5 / norm_length

        max_dist = np.max(shortest_vec_square) ** 0.5 / norm_length

        return rms, max_dist
Ejemplo n.º 11
0
    def _cmp_cartesian_struct(self, s1, s2, l1, l2):
        """
        Once a fit is found, a rms minimizing fit is done to
        ensure the fit is correct. To do this,

        1) The structures are placed into an average lattice
        2) All sites are shifted by the mean
            displacement vector between matched sites.
        3) calculate distances
        4) return rms distance normalized by (V/Natom) ^ 1/3
            and the maximum distance found
        """
        nsites = sum(map(len, s1))

        avg_params = (np.array(l1.lengths_and_angles) +
                      np.array(l2.lengths_and_angles)) / 2

        avg_lattice = Lattice.from_lengths_and_angles(avg_params[0],
                                                      avg_params[1])
        dist = np.zeros([nsites, nsites]) + 100 * nsites
        vec_matrix = np.zeros([nsites, nsites, 3])
        i = 0
        for s1_coords, s2_coords in zip(s1, s2):
            j = len(s1_coords)
            vecs = pbc_shortest_vectors(avg_lattice, s1_coords, s2_coords)
            distances = (np.sum(vecs**2, axis=-1))**0.5
            dist[i:i + j, i:i + j] = distances
            vec_matrix[i:i + j, i:i + j] = vecs
            i += j
        lin = LinearAssignment(dist)
        inds = np.arange(nsites)

        shortest_vecs = vec_matrix[inds, lin.solution, :]
        shortest_vec_square = np.sum(
            (shortest_vecs - np.average(shortest_vecs, axis=0))**2, -1)

        norm_length = (avg_lattice.volume / nsites)**(1 / 3)

        rms = np.average(shortest_vec_square)**0.5 / norm_length

        max_dist = np.max(shortest_vec_square)**0.5 / norm_length

        return rms, max_dist
Ejemplo n.º 12
0
    def _cart_dists(self, s1, s2, l1, l2, mask):
        """
        Finds the cartesian distances normalized by (V/Natom) ^ 1/3
        between two structures on the average lattice of l1 and l2
        s_superset and s_subset are lists of fractional coordinates.
        Minimizes the RMS distance of the matching with an additional
        translation (but doesn't change the mapping)
        returns distances, fractional_translation vector
        """
        #ensure that we always calculate distances from the subset
        #to the superset
        if len(s1) > len(s2):
            s_superset, s_subset, mult = s1, s2, 1
        else:
            s_superset, s_subset, mult = s2, s1, -1
            mask = mask.T
        #create the average lattice
        avg_params = (np.array(l1.lengths_and_angles) +
                      np.array(l2.lengths_and_angles)) / 2
        avg_lattice = Lattice.from_lengths_and_angles(*avg_params)
        norm_length = (avg_lattice.volume / len(s_superset))**(1 / 3)
        mask_val = 1e20 * norm_length * self.stol

        all_d_2 = np.zeros([len(s_superset), len(s_superset)])
        vec_matrix = np.zeros([len(s_superset), len(s_superset), 3])

        #vectors from subset to superset
        #1st index subset, 2nd index superset
        vecs = pbc_shortest_vectors(avg_lattice, s_subset, s_superset)
        vec_matrix[:len(s_subset), :len(s_superset)] = vecs
        vec_matrix[mask] = mask_val
        d_2 = (np.sum(vecs**2, axis=-1))
        all_d_2[:len(s_subset), :len(s_superset)] = d_2
        all_d_2[mask] = mask_val
        lin = LinearAssignment(all_d_2)
        inds = np.arange(len(s_subset))
        #shortest vectors from the subset to the superset
        shortest_vecs = vec_matrix[inds, lin.solution[:len(s_subset)], :]
        translation = np.average(shortest_vecs, axis=0)
        f_translation = avg_lattice.get_fractional_coords(translation)
        shortest_distances = np.sum((shortest_vecs - translation)**2, -1)**0.5
        return shortest_distances / norm_length, f_translation * mult
Ejemplo n.º 13
0
    def get_all_distances(self, fcoords1, fcoords2):
        """
        Returns the distances between two lists of coordinates taking into
        account periodic boundary conditions and the lattice. Note that this
        computes an MxN array of distances (i.e. the distance between each
        point in fcoords1 and every coordinate in fcoords2). This is
        different functionality from pbc_diff.

        Args:
            fcoords1: First set of fractional coordinates. e.g., [0.5, 0.6,
                0.7] or [[1.1, 1.2, 4.3], [0.5, 0.6, 0.7]]. It can be a single
                coord or any array of coords.
            fcoords2: Second set of fractional coordinates.

        Returns:
            2d array of cartesian distances. E.g the distance between
            fcoords1[i] and fcoords2[j] is distances[i,j]
        """
        v, d2 = pbc_shortest_vectors(self, fcoords1, fcoords2, return_d2=True)
        return np.sqrt(d2)
Ejemplo n.º 14
0
    def get_all_distances(self, fcoords1, fcoords2):
        """
        Returns the distances between two lists of coordinates taking into
        account periodic boundary conditions and the lattice. Note that this
        computes an MxN array of distances (i.e. the distance between each
        point in fcoords1 and every coordinate in fcoords2). This is
        different functionality from pbc_diff.

        Args:
            fcoords1: First set of fractional coordinates. e.g., [0.5, 0.6,
                0.7] or [[1.1, 1.2, 4.3], [0.5, 0.6, 0.7]]. It can be a single
                coord or any array of coords.
            fcoords2: Second set of fractional coordinates.

        Returns:
            2d array of cartesian distances. E.g the distance between
            fcoords1[i] and fcoords2[j] is distances[i,j]
        """
        v, d2 = pbc_shortest_vectors(self, fcoords1, fcoords2, return_d2=True)
        return np.sqrt(d2)