def test_converter(transformation, frac_coords): converter = DerivativeMultiLatticeHash(transformation, frac_coords) index = converter.index num_sites = converter.num_sites all_factors = converter.get_all_factors() assert len({tuple(f) for f in all_factors}) == index all_lattice_points = converter.get_lattice_points() assert len({tuple(f) for f in all_lattice_points}) == index all_periodic_sites = converter.get_canonical_sites_list() assert len(set(all_periodic_sites)) == num_sites all_dsites = converter.get_distinct_derivative_sites_list() for dsite in all_dsites: csite, derivative_jimage = converter.hash_derivative_site(dsite, return_image=True) fcoords1 = converter.get_frac_coords(dsite) fcoords2 = ( converter.displacement_set[dsite.site_index] + np.dot(converter.left_inv, csite.factor) + np.dot(converter.hnf, derivative_jimage) ) assert np.allclose(fcoords1, fcoords2) for csite in all_periodic_sites: ind = converter.ravel_canonical_site(csite) csite2 = converter.unravel_to_canonical_site(ind) assert csite2 == csite
def test_converter(): list_transformations = [ np.diag((2, 2, 2)), np.array([[-1, 1, 1], [1, -1, 1], [1, 1, -1]]), np.array([[-1, 1, 1], [1, -1, 1], [-1, -1, 1]]), # negative determinat ] list_frac_coords = [ np.array([[0, 0, 0]]), np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) ] for transformation in list_transformations: for frac_coords in list_frac_coords: converter = DerivativeMultiLatticeHash(transformation, frac_coords) index = converter.index num_sites = converter.num_sites all_factors = converter.get_all_factors() assert len(set([tuple((f)) for f in all_factors])) == index all_lattice_points = converter.get_lattice_points() assert len(set([tuple(f) for f in all_lattice_points])) == index all_periodic_sites = converter.get_canonical_sites_list() assert len(set(all_periodic_sites)) == num_sites all_dsites = converter.get_distinct_derivative_sites_list() for dsite in all_dsites: csite, derivative_jimage = converter.hash_derivative_site( dsite, return_image=True) fcoords1 = converter.get_frac_coords(dsite) fcoords2 = (converter.displacement_set[dsite.site_index] + np.dot(converter.left_inv, csite.factor) + np.dot(converter.hnf, derivative_jimage)) assert np.allclose(fcoords1, fcoords2) for csite in all_periodic_sites: ind = converter.ravel_canonical_site(csite) csite2 = converter.unravel_to_canonical_site(ind) assert csite2 == csite
class DerivativeStructurePermutation: """ Permutation Representation of space group of superlattice Parameters ---------- hnf: array, (dim, dim) Hermite normal form(lower triangular) displacement_set: array, (num_site_parent, dim) fractinal coordinates in primitive cell of base structure rotations: array, (# of symmetry operations, dim, dim) rotations with primitive basis for base structure translations: array, (# of symmetry operations, dim) translations with primitive basis for base structure Attributes ---------- dim : int dimention of lattice index: int # of parent multilattice in super lattice num_site: int # of sites in unit cell of superlattice """ def __init__( self, hnf: np.ndarray, displacement_set: np.ndarray, rotations: np.ndarray, translations: np.ndarray, ): self.hnf = hnf self.num_sites_base = len(displacement_set) # TODO: check each site in displacement_set is in [0, 1)^dim self.displacement_set = displacement_set self.dhash = DerivativeMultiLatticeHash(self.hnf, self.displacement_set) self.rotations, self.translations = self._get_superlattice_invariant_subgroup( rotations, translations ) self.list_dsites = self.dhash.get_distinct_derivative_sites_list() self.list_csites = self.dhash.get_canonical_sites_list() self._prm_t = self._get_translation_permutations() self.prm_rigid = self._get_rigid_permutations() @property def dim(self): return self.dhash.dim @property def index(self): return self.dhash.index @property def num_sites(self): return self.dhash.num_sites @property def prm_t(self): return self._prm_t def _get_superlattice_invariant_subgroup( self, rotations: np.ndarray, translations: np.ndarray ): valid_rotations = [] valid_translations = [] for R, tau in zip(rotations, translations): if not is_same_lattice(np.dot(R, self.hnf), self.hnf): continue valid_rotations.append(R) valid_translations.append(tau) assert len(rotations) % len(valid_rotations) == 0 return np.array(valid_rotations), np.array(valid_translations) def _get_translation_permutations(self): list_permutations = [] for add_factor in self.dhash.get_all_factors(): new_list_csites = [] for csite in self.list_csites: new_factor = self.dhash.modulus_factor( np.array(csite.factor) + np.array(add_factor) ) new_csite = CanonicalSite(csite.site_index, new_factor) new_list_csites.append(new_csite) # permutation represenation perm = [self.dhash.ravel_canonical_site(csite) for csite in new_list_csites] assert is_permutation(perm) list_permutations.append(perm) # assume list_permutations[0] is identity assert is_identity_permutation(list_permutations[0]) return list_permutations def _get_rigid_permutations(self): identity = list(range(self.num_sites)) list_permutations = [ identity, ] for R, tau in zip(self.rotations, self.translations): new_list_csites = [] for dsite in self.list_dsites: frac_coord = self.displacement_set[dsite.site_index] + np.array(dsite.jimage) acted_frac_coord = np.dot(R, frac_coord) + tau new_csite = self.dhash.hash_frac_coords(acted_frac_coord) assert new_csite is not None new_list_csites.append(new_csite) perm = [self.dhash.ravel_canonical_site(csite) for csite in new_list_csites] assert is_permutation(perm) if perm not in list_permutations: list_permutations.append(perm) # this set of permutations is not group! return list_permutations def get_symmetry_operation_permutations(self): """ return permutation representation of the symmetry group of the supercell """ list_permutations = [] for p1 in self.prm_t: for p2 in self.prm_rigid: perm = product_permutations(p1, p2) assert perm not in list_permutations list_permutations.append(perm) # assume list_permutations[0] is identity assert is_identity_permutation(list_permutations[0]) return list_permutations