Example #1
0
    def get_nn_info(self, structure: Structure, n: int) -> List[Dict]:
        """
        Get all near-neighbor sites as well as the associated image locations
        and weights of the site with index n using the closest neighbor
        distance-based method.

        Args:
            structure (Structure): input structure.
            n (int): index of site for which to determine near
                neighbors.

        Returns:
            (list of tuples (Site, array, float)):
            tuples, each one of which represents a neighbor site, its image location,
                and its weight.
        """

        site = structure[n]
        neighs_dists = structure.get_neighbors(site, self.cutoff)

        siw = []
        for nn in neighs_dists:
            siw.append({
                "site": nn,
                "image": self._get_image(structure, nn),
                "weight": nn.nn_distance,
                "site_index": self._get_original_site(structure, nn),
            })
        return siw
Example #2
0
def get_coordination_distances(structure: Structure,
                               atom_index: int,
                               cutoff: float) -> dict:
    """Calculated the coordination environment at the given atomic index.

    Args:
        structure (Structure):
            pmg Structure class object
        atom_index (int):
            The atomic index
        cutoff (float):
            Radius of a sphere in which atoms are considered as neighbors

    Return:
        Dict composed of specie name keys and distance list.
        E.g., {"Mg": [1.82, 1.82, 1.82, 1.82], ..}
    """
    neighbors = structure.get_neighbors(
        structure.sites[atom_index], cutoff, include_index=True)

    coordination_distances = defaultdict(list)

    for n in neighbors:
        # remove oxidation state from species_string, e.g., Mg2+ -> Mg
        specie = ''.join([i for i in str(n._species) if i.isalpha()])
        # float is needed as the numpy.float is not compatible with the
        # combination of OrderedDict and yaml. Otherwise, the interstitial.yaml
        # shows ugly printing.
        coordination_distances[specie].append(round(float(n[1]), 2))

    for distances in coordination_distances.values():
        distances.sort()

    return dict(coordination_distances)
Example #3
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
        recip = Structure(self._s.lattice.reciprocal_lattice, ["H"], [np.array([0, 0, 0])])
        recip_nn = recip.get_neighbors(recip[0], self._gmax)

        for (n, dist) in recip_nn:
            gvect = n.coords
            gsquare = np.linalg.norm(gvect) ** 2

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

            gvect_tile = np.tile(gvect, (numsites, 1))
            gvectdot = np.sum(gvect_tile * coords, 1)
            #calculate the structure factor
            sfactor = np.zeros((numsites, numsites))
            sreal = 0.0
            simag = 0.0
            for i in xrange(numsites):
                qi = self._oxi_states[i]
                g_dot_i = gvectdot[i]
                sfactor[i, i] = qi * qi
                sreal += qi * cos(g_dot_i)
                simag += qi * sin(g_dot_i)

                for j in xrange(i + 1, numsites):
                    qj = self._oxi_states[j]
                    exparg = g_dot_i - gvectdot[j]
                    cosa = cos(exparg)
                    sina = sin(exparg)
                    sfactor[i, j] = qi * qj * (cosa + sina)
                    """
                    Uses the property that when sitei and sitej are switched,
                    exparg' == - exparg. This implies 
                    cos (exparg') = cos (exparg) and
                    sin (exparg') = - sin (exparg)
                    
                    Halves all computations.
                    """
                    sfactor[j, i] = qi * qj * (cosa - sina)

            erecip += expval / gsquare * sfactor
            pref = 2 * expval / gsquare * np.array(self._oxi_states)
            factor = prefactor * pref * (sreal * np.sin(gvectdot) - simag * np.cos(gvectdot)) * EwaldSummation.CONV_FACT
            forces += np.tile(factor, (3, 1)).transpose() * gvect_tile

        return (erecip * prefactor * EwaldSummation.CONV_FACT , forces)
Example #4
0
def analyze(topology: Structure, skin: float = 5e-3) -> List[Molecule]:
    # containers for the output
    fragments = []
    # we iterate over non-dummies
    dmat = topology.distance_matrix
    dummies = numpy.array(topology.indices_from_symbol("X"))
    not_dummies = numpy.array(
        [i for i in range(len(topology)) if i not in dummies])
    # initialize and set tags
    tags = numpy.zeros(len(topology), dtype=int)
    tags[dummies] = dummies + 1
    topology.add_site_property(property_name="tags", values=tags)
    # TODO : site properties
    # get the distances between centers and connections
    distances = dmat[not_dummies][:, dummies]
    coordinations = numpy.array(topology.atomic_numbers)[not_dummies]
    partitions = numpy.argsort(distances, axis=1)
    for center_idx, best_dummies in enumerate(partitions):
        coordination = coordinations[center_idx]
        if coordination < len(best_dummies):
            best_dummies = best_dummies[:coordination]
        cutoff = distances[center_idx][best_dummies].max() + skin
        # now extract the corresponding fragment
        fragment_center = topology.sites[not_dummies[center_idx]]
        fragment_sites = topology.get_neighbors(fragment_center, r=cutoff)
        # some topologies in the RCSR have a crazy size
        # we ignore them with the heat of a thousand suns
        assert len(
            fragment_sites) <= 12, "Fragment size larger than limit of 12."
        # store as molecule to use the point group analysis
        fragment = Molecule.from_sites(
            fragment_sites)  #, charge=1, spin_multiplicity=1)
        # this is needed because X have no mass, leading to a
        # symmetrization error.
        fragment.replace_species({"X": "He"})
        pg = PointGroupAnalyzer(fragment, tolerance=0.1)
        # from pymatgen.io.ase import AseAtomsAdaptor
        # view(AseAtomsAdaptor.get_atoms(fragment))
        if pg.sch_symbol == "C1":
            raise ("NoSymm")
        fragment.replace_species({"He": "X"})
        fragments.append(Fragment(atoms=fragment, symmetry=pg, name="slot"))
    return fragments