def test_spherical(self): a = PointGroupAnalyzer(CH4) self.assertEqual(a.sch_symbol, "Td") self.assertEqual(len(a.get_pointgroup()), 24) a = PointGroupAnalyzer(PF6) self.assertEqual(a.sch_symbol, "Oh") self.assertEqual(len(a.get_pointgroup()), 48) m = Molecule.from_file(os.path.join(test_dir_mol, "c60.xyz")) a = PointGroupAnalyzer(m) self.assertEqual(a.sch_symbol, "Ih") cube_species = ["C", "C", "C", "C", "C", "C", "C", "C"] cube_coords = [ [0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [0, 0, 1], [0, 1, 1], [1, 0, 1], [1, 1, 1], ] m = Molecule(cube_species, cube_coords) a = PointGroupAnalyzer(m, 0.1) self.assertEqual(a.sch_symbol, "Oh")
def test_asym_top(self): coords = [ [0.000000, 0.000000, 0.000000], [0.000000, 0.000000, 1.08], [1.026719, 0.000000, -0.363000], [-0.513360, -0.889165, -0.363000], [-0.513360, 0.889165, -0.363000], ] mol = Molecule(["C", "H", "F", "Br", "Cl"], coords) a = PointGroupAnalyzer(mol) self.assertEqual(a.sch_symbol, "C1") self.assertEqual(len(a.get_pointgroup()), 1) coords = [ [0.000000, 0.000000, 1.08], [1.026719, 0.000000, -0.363000], [-0.513360, -0.889165, -0.363000], [-0.513360, 0.889165, -0.363000], ] cs_mol = Molecule(["H", "F", "Cl", "Cl"], coords) a = PointGroupAnalyzer(cs_mol) self.assertEqual(a.sch_symbol, "Cs") self.assertEqual(len(a.get_pointgroup()), 2) a = PointGroupAnalyzer(C2H2F2Br2) self.assertEqual(a.sch_symbol, "Ci") self.assertEqual(len(a.get_pointgroup()), 2)
def test_spherical(self): a = PointGroupAnalyzer(CH4) self.assertEqual(a.sch_symbol, "Td") self.assertEqual(len(a.get_pointgroup()), 24) a = PointGroupAnalyzer(PF6) self.assertEqual(a.sch_symbol, "Oh") self.assertEqual(len(a.get_pointgroup()), 48) m = Molecule.from_file(os.path.join(test_dir_mol, "c60.xyz")) a = PointGroupAnalyzer(m) self.assertEqual(a.sch_symbol, "Ih")
def test_spherical(self): a = PointGroupAnalyzer(CH4) self.assertEqual(a.sch_symbol, "Td") self.assertEqual(len(a.get_pointgroup()), 24) a = PointGroupAnalyzer(PF6) self.assertEqual(a.sch_symbol, "Oh") self.assertEqual(len(a.get_pointgroup()), 48) m = Molecule.from_file(os.path.join(test_dir_mol, "c60.xyz")) a = PointGroupAnalyzer(m) self.assertEqual(a.sch_symbol, "Ih")
def test_dihedral(self): a = PointGroupAnalyzer(C2H4) self.assertEqual(a.sch_symbol, "D2h") self.assertEqual(len(a.get_pointgroup()), 8) a = PointGroupAnalyzer(BF3) self.assertEqual(a.sch_symbol, "D3h") self.assertEqual(len(a.get_pointgroup()), 12) m = Molecule.from_file(os.path.join(test_dir_mol, "b12h12.xyz")) a = PointGroupAnalyzer(m) self.assertEqual(a.sch_symbol, "D5d")
def test_dihedral(self): a = PointGroupAnalyzer(C2H4) self.assertEqual(a.sch_symbol, "D2h") self.assertEqual(len(a.get_pointgroup()), 8) a = PointGroupAnalyzer(BF3) self.assertEqual(a.sch_symbol, "D3h") self.assertEqual(len(a.get_pointgroup()), 12) m = Molecule.from_file(os.path.join(test_dir_mol, "b12h12.xyz")) a = PointGroupAnalyzer(m) self.assertEqual(a.sch_symbol, "Ih")
def test_symmetrize_molecule2(self): np.random.seed(77) distortion = np.random.randn(len(C2H2F2Br2), 3) / 20 dist_mol = Molecule(C2H2F2Br2.species, C2H2F2Br2.cart_coords + distortion) PA1 = PointGroupAnalyzer(C2H2F2Br2, tolerance=0.1) self.assertTrue(PA1.get_pointgroup().sch_symbol == "Ci") PA2 = PointGroupAnalyzer(dist_mol, tolerance=0.1) self.assertTrue(PA2.get_pointgroup().sch_symbol == "C1") eq = iterative_symmetrize(dist_mol, tolerance=0.3) PA3 = PointGroupAnalyzer(eq["sym_mol"], tolerance=0.1) self.assertTrue(PA3.get_pointgroup().sch_symbol == "Ci")
def test_symmetrize_molecule2(self): np.random.seed(77) distortion = np.random.randn(len(C2H2F2Br2), 3) / 20 dist_mol = Molecule(C2H2F2Br2.species, C2H2F2Br2.cart_coords + distortion) PA1 = PointGroupAnalyzer(C2H2F2Br2, tolerance=0.1) self.assertTrue(PA1.get_pointgroup().sch_symbol == 'Ci') PA2 = PointGroupAnalyzer(dist_mol, tolerance=0.1) self.assertTrue(PA2.get_pointgroup().sch_symbol == 'C1') eq = iterative_symmetrize(dist_mol, tolerance=0.3) PA3 = PointGroupAnalyzer(eq['sym_mol'], tolerance=0.1) self.assertTrue(PA3.get_pointgroup().sch_symbol == 'Ci')
def get_symmetry(mol, already_oriented=False): ''' Return a list of SymmOps for a molecule's point symmetry already_oriented: whether or not the principle axes of mol are already reoriented ''' pga = PointGroupAnalyzer(mol) #Handle linear molecules if '*' in pga.sch_symbol: if already_oriented == False: #Reorient the molecule oriented_mol, P = reoriented_molecule(mol) pga = PointGroupAnalyzer(oriented_mol) pg = pga.get_pointgroup() symm_m = [] for op in pg: symm_m.append(op) #Add 12-fold and reflections in place of ininitesimal rotation for axis in [[1, 0, 0], [0, 1, 0], [0, 0, 1]]: op = SymmOp.from_rotation_and_translation(aa2matrix(axis, pi / 6), [0, 0, 0]) if pga.is_valid_op(op): symm_m.append(op) #Any molecule with infinitesimal symmetry is linear; #Thus, it possess mirror symmetry for any axis perpendicular #To the rotational axis. pymatgen does not add this symmetry #for all linear molecules - for example, hydrogen if axis == [1, 0, 0]: symm_m.append(SymmOp.from_xyz_string('x,-y,z')) symm_m.append(SymmOp.from_xyz_string('x,y,-z')) elif axis == [0, 1, 0]: symm_m.append(SymmOp.from_xyz_string('-x,y,z')) symm_m.append(SymmOp.from_xyz_string('x,y,-z')) elif axis == [0, 0, 1]: symm_m.append(SymmOp.from_xyz_string('-x,y,z')) symm_m.append(SymmOp.from_xyz_string('x,-y,z')) #Generate a full list of SymmOps for the molecule's pointgroup symm_m = generate_full_symmops(symm_m, 1e-3) break #Reorient the SymmOps into mol's original frame if not already_oriented: new = [] for op in symm_m: new.append(P.inverse * op * P) return new elif already_oriented: return symm_m #Handle nonlinear molecules else: pg = pga.get_pointgroup() symm_m = [] for op in pg: symm_m.append(op) return symm_m
def test_cyclic(self): a = PointGroupAnalyzer(H2O2) self.assertEqual(a.sch_symbol, "C2") self.assertEqual(len(a.get_pointgroup()), 2) a = PointGroupAnalyzer(H2O) self.assertEqual(a.sch_symbol, "C2v") self.assertEqual(len(a.get_pointgroup()), 4) a = PointGroupAnalyzer(NH3) self.assertEqual(a.sch_symbol, "C3v") self.assertEqual(len(a.get_pointgroup()), 6) cs2 = Molecule.from_file(os.path.join(test_dir_mol, "Carbon_Disulfide.xyz")) a = PointGroupAnalyzer(cs2, eigen_tolerance=0.001) self.assertEqual(a.sch_symbol, "C2v")
def test_cyclic(self): a = PointGroupAnalyzer(H2O2) self.assertEqual(a.sch_symbol, "C2") self.assertEqual(len(a.get_pointgroup()), 2) a = PointGroupAnalyzer(H2O) self.assertEqual(a.sch_symbol, "C2v") self.assertEqual(len(a.get_pointgroup()), 4) a = PointGroupAnalyzer(NH3) self.assertEqual(a.sch_symbol, "C3v") self.assertEqual(len(a.get_pointgroup()), 6) cs2 = Molecule.from_file(test_dir_mol / "Carbon_Disulfide.xyz") a = PointGroupAnalyzer(cs2, eigen_tolerance=0.001) self.assertEqual(a.sch_symbol, "C2v")
def test_spherical(self): a = PointGroupAnalyzer(CH4) self.assertEqual(a.sch_symbol, "Td") self.assertEqual(len(a.get_pointgroup()), 24) a = PointGroupAnalyzer(PF6) self.assertEqual(a.sch_symbol, "Oh") self.assertEqual(len(a.get_pointgroup()), 48) m = Molecule.from_file(os.path.join(test_dir_mol, "c60.xyz")) a = PointGroupAnalyzer(m) self.assertEqual(a.sch_symbol, "Ih") cube_species = ["C", "C", "C", "C", "C", "C", "C", "C"] cube_coords = [[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [0, 0, 1], [0, 1, 1], [1, 0, 1], [1, 1, 1]] m = Molecule(cube_species, cube_coords) a = PointGroupAnalyzer(m, 0.1) self.assertEqual(a.sch_symbol, "Oh")
def symmetry_information(self): """ Returns symmetry information of the molecule as for example its point group :return info: a dictionary with symmetry informations """ mol = mg.Molecule(self.atoms[:, 3], self.atoms[:, :3]) analyzer = PointGroupAnalyzer(mol) info = {'point group': analyzer.get_pointgroup()} return info
def test_asym_top(self): coords = [[0.000000, 0.000000, 0.000000], [0.000000, 0.000000, 1.08], [1.026719, 0.000000, -0.363000], [-0.513360, -0.889165, -0.363000], [-0.513360, 0.889165, -0.363000]] mol = Molecule(["C", "H", "F", "Br", "Cl"], coords) a = PointGroupAnalyzer(mol) self.assertEqual(a.sch_symbol, "C1") self.assertEqual(len(a.get_pointgroup()), 1) coords = [[0.000000, 0.000000, 1.08], [1.026719, 0.000000, -0.363000], [-0.513360, -0.889165, -0.363000], [-0.513360, 0.889165, -0.363000]] cs_mol = Molecule(["H", "F", "Cl", "Cl"], coords) a = PointGroupAnalyzer(cs_mol) self.assertEqual(a.sch_symbol, "Cs") self.assertEqual(len(a.get_pointgroup()), 2) a = PointGroupAnalyzer(C2H2F2Br2) self.assertEqual(a.sch_symbol, "Ci") self.assertEqual(len(a.get_pointgroup()), 2)
def structure_symmetry(): structs, fnames = read_structures() multi_structs(structs, fnames) for struct, fname in zip(structs, fnames): if isinstance(struct, Structure): sa = SpacegroupAnalyzer(struct) print("{:<20} : {}".format('File Name', fname)) print("{:<20} : {:<15}".format('Structure Type', 'periodicity')) print("{:<20} : {:<15}".format('Lattice Type', sa.get_lattice_type())) print("{:<20} : {:<15d}".format('Space Group ID', sa.get_space_group_number())) print("{:<20} : {:<15}".format('International Symbol', sa.get_space_group_symbol())) print("{:<20} : {:15}".format('Hall Symbol', sa.get_hall())) sepline() if isinstance(struct, Molecule): print("{:<20} : {}".format('File Name', fname)) pga = PointGroupAnalyzer(struct) print("{:<20} : {:<15}".format('Structure Type', 'non-periodicity')) print("{:<20} : {:<15}".format('International Symbol', str(pga.get_pointgroup()))) return True
def get_symmetry(mol, already_oriented=False): """ Return a molecule's point symmetry. Note: for linear molecules, infinitessimal rotations are treated as 6-fold rotations, which works for 3d and 2d point groups. Args: mol: a Molecule object already_oriented: whether or not the principle axes of mol are already reoriented. Can save time if True, but is not required. Returns: a list of SymmOp objects which leave the molecule unchanged when applied """ # For single atoms, we cannot represent the point group using a list of operations if len(mol) == 1: return [] pga = PointGroupAnalyzer(mol) # Handle linear molecules if "*" in pga.sch_symbol: if not already_oriented: # Reorient the molecule oriented_mol, P = reoriented_molecule(mol) pga = PointGroupAnalyzer(oriented_mol) pg = pga.get_pointgroup() symm_m = [] for op in pg: symm_m.append(op) # Add 12-fold and reflections in place of ininitesimal rotation for axis in [[1, 0, 0], [0, 1, 0], [0, 0, 1]]: # op = SymmOp.from_rotation_and_translation(aa2matrix(axis, np.pi/6), [0,0,0]) m1 = Rotation.from_rotvec(np.pi / 6 * axis).as_matrix() op = SymmOp.from_rotation_and_translation(m1, [0, 0, 0]) if pga.is_valid_op(op): symm_m.append(op) # Any molecule with infinitesimal symmetry is linear; # Thus, it possess mirror symmetry for any axis perpendicular # To the rotational axis. pymatgen does not add this symmetry # for all linear molecules - for example, hydrogen if axis == [1, 0, 0]: symm_m.append(SymmOp.from_xyz_string("x,-y,z")) symm_m.append(SymmOp.from_xyz_string("x,y,-z")) #r = SymmOp.from_xyz_string("-x,y,-z") elif axis == [0, 1, 0]: symm_m.append(SymmOp.from_xyz_string("-x,y,z")) symm_m.append(SymmOp.from_xyz_string("x,y,-z")) #r = SymmOp.from_xyz_string("-x,-y,z") elif axis == [0, 0, 1]: symm_m.append(SymmOp.from_xyz_string("-x,y,z")) symm_m.append(SymmOp.from_xyz_string("x,-y,z")) #r = SymmOp.from_xyz_string("x,-y,-z") # Generate a full list of SymmOps for the molecule's pointgroup symm_m = generate_full_symmops(symm_m, 1e-3) break # Reorient the SymmOps into mol's original frame if not already_oriented: new = [] for op in symm_m: new.append(P.inverse * op * P) return new elif already_oriented: return symm_m # Handle nonlinear molecules else: pg = pga.get_pointgroup() symm_m = [] for op in pg: symm_m.append(op) return symm_m
from pymatgen.symmetry.analyzer import PointGroupAnalyzer from pymatgen.core.structure import Molecule for i, g in enumerate(geo): mol = Molecule(atomic_nums, g) pga = PointGroupAnalyzer(mol) if str(pga.get_pointgroup()) != "C1": print(i)
already_oriented=already_oriented) is True: allowed.append(o) #Return the array of allowed orientations. If there are none, return False if allowed == []: return False else: return allowed #Test Functionality if __name__ == "__main__": #--------------------------------------------------- #Test cases: water, methane, and c60 via pymatgen h2o = Molecule.from_file('xyz/water.xyz') pga_h2o = PointGroupAnalyzer(h2o) pg_h2o = pga_h2o.get_pointgroup() c60 = Molecule.from_file('xyz/C60-0.xyz') pga_c60 = PointGroupAnalyzer(c60) pg_c60 = pga_c60.get_pointgroup() h2 = Molecule.from_file('xyz/hydrogen.xyz') pga_h2 = PointGroupAnalyzer(h2) pg_h2 = pga_h2.get_pointgroup() ch4 = Molecule.from_file('xyz/methane.xyz') pga_ch4 = PointGroupAnalyzer(ch4) pg_ch4 = pga_ch4.get_pointgroup() rand_mol = Molecule.from_file('xyz/random.xyz') pga_rand_mol = PointGroupAnalyzer(rand_mol) pg_rand_mol = pga_rand_mol.get_pointgroup() #Testing water mol = deepcopy(c60)
def _get_SiteEnvironments(struct: PymatgenStructure, cutoff: float, PBC: List[bool], get_permutations: bool = True, eigen_tol: float = 1e-5) -> List[Dict[str, Any]]: """ Used to extract information about both primitive cells and data points. Extract local environments from Structure object by calculating neighbors based on gaussian distance. For primitive cell, Different permutations of the neighbors are calculated and will be later will mapped for data point in the _SiteEnvironment.get_mapping() function. site types , Parameters ---------- struct: PymatgenStructure Pymatgen Structure object of the primitive cell used for calculating neighbors from lattice transformations.It also requires site_properties attribute with "Sitetypes"(Active or spectator site). cutoff : float cutoff distance in angstrom for collecting local environment. pbc : np.ndarray Periodic boundary condition get_permutations : bool (default True) Whether to find permuted neighbor list or not. eigen_tol : float (default 1e-5) Tolerance for eigenanalysis of point group analysis in pymatgen. Returns ------ site_envs : List[Dict[str, Any]] list of local_env class """ try: from pymatgen import Molecule from pymatgen.symmetry.analyzer import PointGroupAnalyzer except: raise ImportError("This class requires pymatgen to be installed.") pbc = np.array(PBC) structure = struct neighbors = structure.get_all_neighbors(cutoff, include_index=True) symbols = structure.species site_idxs = [ i for i, sitetype in enumerate(structure.site_properties['SiteTypes']) if sitetype == 'A1' ] site_sym_map = {} sym_site_map = {} for i, new_ele in enumerate(structure.species): sym_site_map[new_ele] = structure.site_properties['SiteTypes'][i] site_sym_map[structure.site_properties['SiteTypes'][i]] = new_ele site_envs = [] for site_idx in site_idxs: local_env_sym = [symbols[site_idx]] local_env_xyz = [structure[site_idx].coords] local_env_dist = [0.0] local_env_sitemap = [site_idx] for n in neighbors[site_idx]: # if PBC condition is fulfilled.. c = np.around(n[0].frac_coords, 10) withinPBC = np.logical_and(0 <= c, c < 1) if np.all(withinPBC[~pbc]): local_env_xyz.append(n[0].coords) local_env_sym.append(n[0].specie) local_env_dist.append(n[1]) local_env_sitemap.append(n[2]) local_env_xyz = np.subtract(local_env_xyz, np.mean(local_env_xyz, 0)) perm = [] if get_permutations: finder = PointGroupAnalyzer( Molecule(local_env_sym, local_env_xyz), eigen_tolerance=eigen_tol) pg = finder.get_pointgroup() for i, op in enumerate(pg): newpos = op.operate_multi(local_env_xyz) perm.append(np.argmin(cdist(local_env_xyz, newpos), axis=1).tolist()) site_env = { 'pos': local_env_xyz, 'sitetypes': [sym_site_map[s] for s in local_env_sym], 'env2config': local_env_sitemap, 'permutations': perm, 'dist': local_env_dist } site_envs.append(site_env) return site_envs
def _GetSiteEnvironments(cls, coord, cell, SiteTypes, cutoff, pbc, get_permutations=True, eigen_tol=1e-5): """Extract local environments from primitive cell Parameters ---------- coord : n x 3 list or numpy array of scaled positions. n is the number of atom. cell : 3 x 3 list or numpy array SiteTypes : n list of string. String must be S or A followed by a number. S indicates a spectator sites and A indicates a active sites. cutoff : float. cutoff distance in angstrom for collecting local environment. pbc : list of boolean. Periodic boundary condition get_permutations : boolean. Whether to find permutatated neighbor list or not. eigen_tol : tolerance for eigenanalysis of point group analysis in pymatgen. Returns ------ list of local_env : list of local_env class """ #%% Check error assert isinstance(coord, (list, np.ndarray)) assert isinstance(cell, (list, np.ndarray)) assert len(coord) == len(SiteTypes) #%% Initialize # TODO: Technically, user doesn't even have to supply site index, because # pymatgen can be used to automatically categorize sites.. coord = np.mod(coord, 1) pbc = np.array(pbc) #%% Map sites to other elements.. # TODO: Available pymatgne functions are very limited when DummySpecie is # involved. This may be perhaps fixed in the future. Until then, we # simply bypass this by mapping site to an element # Find available atomic number to map site to it availableAN = [i + 1 for i in reversed(range(0, 118))] # Organize Symbols and record mapping symbols = [] site_idxs = [] SiteSymMap = {} # mapping SymSiteMap = {} for i, SiteType in enumerate(SiteTypes): if SiteType not in SiteSymMap: symbol = Element.from_Z(availableAN.pop()) SiteSymMap[SiteType] = symbol SymSiteMap[symbol] = SiteType else: symbol = SiteSymMap[SiteType] symbols.append(symbol) if 'A' in SiteType: site_idxs.append(i) #%% Get local environments of each site # Find neighbors and permutations using pymatgen lattice = Lattice(cell) structure = Structure(lattice, symbols, coord) neighbors = structure.get_all_neighbors(cutoff, include_index=True) site_envs = [] for site_idx in site_idxs: local_env_sym = [symbols[site_idx]] local_env_xyz = [structure[site_idx].coords] local_env_dist = [0.0] local_env_sitemap = [site_idx] for n in neighbors[site_idx]: # if PBC condition is fulfilled.. c = np.around(n[0].frac_coords, 10) withinPBC = np.logical_and(0 <= c, c < 1) if np.all(withinPBC[~pbc]): local_env_xyz.append(n[0].coords) local_env_sym.append(n[0].specie) local_env_dist.append(n[1]) local_env_sitemap.append(n[2]) local_env_xyz = np.subtract(local_env_xyz, np.mean(local_env_xyz, 0)) perm = [] if get_permutations: finder = PointGroupAnalyzer(Molecule(local_env_sym, local_env_xyz), eigen_tolerance=eigen_tol) pg = finder.get_pointgroup() for i, op in enumerate(pg): newpos = op.operate_multi(local_env_xyz) perm.append( np.argmin(cdist(local_env_xyz, newpos), axis=1).tolist()) site_env = { 'pos': local_env_xyz, 'sitetypes': [SymSiteMap[s] for s in local_env_sym], 'env2config': local_env_sitemap, 'permutations': perm, 'dist': local_env_dist } site_envs.append(site_env) return site_envs
mo.apply_operation(op) if orientation_in_wyckoff_position(mo, sg, index, exact_orientation=True, already_oriented=already_oriented) is True: allowed.append(o) #Return the array of allowed orientations. If there are none, return False if allowed == []: return False else: return allowed #Test Functionality if __name__ == "__main__": #--------------------------------------------------- #Test cases: water, methane, and c60 via pymatgen h2o = Molecule.from_file('xyz/water.xyz') pga_h2o = PointGroupAnalyzer(h2o) pg_h2o = pga_h2o.get_pointgroup() #from ase.build import molecule #Testing water mol = get_ase_molecule("H2O") print("Original molecule:") print(mol) print() #Apply random rotation to avoid lucky results R = aa2matrix(1,1,random=True) R_op = SymmOp.from_rotation_and_translation(R,[0,0,0]) mol.apply_operation(R_op) print("Rotated molecule:") print(mol)