def _cart_dists(self, s1, s2, avg_lattice, mask, normalization, lll_frac_tol=None): """ 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") # vectors are from s2 to s1 vecs, d_2 = pbc_shortest_vectors(avg_lattice, s2, s1, mask, return_d2=True, lll_frac_tol=lll_frac_tol) 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
def _cart_dists(self, s1, s2, avg_lattice, mask, normalization, lll_frac_tol=None): """ 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") # vectors are from s2 to s1 vecs, d_2 = pbc_shortest_vectors(avg_lattice, s2, s1, mask, return_d2=True, lll_frac_tol=lll_frac_tol) 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
def get_ionic_pol_change(struct2, struct1, psp_table, extra_trans=None): """should already be translated""" LOGGER.info("Finding ionic change in polarization") if extra_trans is None: extra_trans = np.array([0., 0., 0.]) else: # convert extra_trans to cartesian extra_trans = np.array(extra_trans) * np.array(struct2.lattice.abc) mask = get_mask(struct2, struct1) vecs, d_2 = pbc_shortest_vectors(struct2.lattice, struct2.frac_coords, struct1.frac_coords, mask, return_d2=True, lll_frac_tol=[0.4, 0.4, 0.4]) lin = LinearAssignment(d_2) s = lin.solution species = [struct1[i].species_string for i in s] short_vecs = vecs[np.arange(len(s)), s] LOGGER.debug("Displacements:") LOGGER.debug(short_vecs) pol_change = np.array([0., 0., 0.]) for v, sp in zip(short_vecs, species): pol_change += (v - extra_trans) * psp_table.pseudo_with_symbol(sp).Z_val LOGGER.debug("{}\t{}\t{}".format( sp, psp_table.pseudo_with_symbol(sp).Z_val, v)) return (ECHARGE * 10**20) * pol_change / struct2.lattice.volume