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
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)
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)
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