def compare_wyckoffs(num1, num2, dim=3): """Given 2 groups, return whether the second point group has equal or greater symmetry than the first group.""" from numpy import allclose if num1 == "???": print("Error: invalid value for num1 passed to compare_wyckoffs") return if num2 == "???": return False # Get general positions for both groups if dim == 3: from pyxtal.symmetry import get_wyckoffs g1 = get_wyckoffs(num1)[0] g2 = get_wyckoffs(num2)[0] elif dim == 2: from pyxtal.symmetry import get_layer g1 = get_layer(num1)[0] g2 = get_layer(num2)[0] elif dim == 1: from pyxtal.symmetry import get_rod g1 = get_rod(num1)[0] g2 = get_rod(num2)[0] elif dim == 0: from pyxtal.symmetry import get_point g1 = get_point(num1)[0] g2 = get_point(num2)[0] # If group 2 has higher symmetry if len(g2) > len(g1): return True # Compare point group operations for i, op2 in enumerate(g2): op1 = g1[i] m1 = op1.rotation_matrix m2 = op2.rotation_matrix if not allclose(m1, m2): return False return True
def check_struct_group(crystal, group, dim=3, tol=1e-2): # Supress pymatgen/numpy complex casting warnings from pyxtal.crystal import random_crystal from pyxtal.molecular_crystal import molecular_crystal from copy import deepcopy import warnings with warnings.catch_warnings(): warnings.simplefilter("ignore") """Given a pymatgen structure, group number, and dimension, return whether or not the structure matches the group number.""" if isinstance(crystal, (random_crystal, molecular_crystal)): lattice = crystal.struct.lattice.matrix if dim != 0: old_coords = deepcopy(crystal.struct.frac_coords) old_species = deepcopy(crystal.struct.atomic_numbers) elif dim == 0: old_coords = deepcopy(crystal.cart_coords) old_species = deepcopy(crystal.species) else: lattice = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) old_coords = np.array(crystal) old_species = ["C"] * len(old_coords) from pyxtal.symmetry import distance from pyxtal.symmetry import filtered_coords from copy import deepcopy PBC = [1, 1, 1] # Obtain the generators for the group if dim == 3: from pyxtal.symmetry import get_wyckoffs generators = get_wyckoffs(group)[0] elif dim == 2: from pyxtal.symmetry import get_layer generators = get_layer(group)[0] PBC = [1, 1, 0] elif dim == 1: from pyxtal.symmetry import get_rod generators = get_rod(group)[0] PBC = [0, 0, 1] elif dim == 0: from pyxtal.symmetry import Group generators = Group(group, dim=0)[0] PBC = [0, 0, 0] # TODO: Add check for lattice symmetry # Apply SymmOps to generate new points # old_coords = filtered_coords(struct.frac_coords,PBC=PBC) new_coords = [] new_species = [] for i, point in enumerate(old_coords): for j, op in enumerate(generators): if j != 0: new_coords.append(op.operate(point)) new_species.append(old_species[i]) # new_coords = filtered_coords(new_coords,PBC=PBC) # Check that all points in new list are still in old failed = False i_list = list(range(len(new_coords))) for i, point1 in enumerate(new_coords): found = False for j, point2 in enumerate(old_coords): if new_species[i] == old_species[j]: difference = filtered_coords(point2 - point1, PBC=PBC) if distance(difference, lattice, PBC=PBC) <= tol: found = True break if found is False: failed = True break if failed is False: return True else: return False
def test_molecular_1D(): global outstructs global outstrings print( "=== Testing generation of molecular 1D crystals. This may take some time. ===" ) from time import time from spglib import get_symmetry_dataset from pyxtal.symmetry import get_rod from pyxtal.molecular_crystal import molecular_crystal_1D from pymatgen.symmetry.analyzer import SpacegroupAnalyzer slow = [] failed = [] print(" Rod group | Gen sg. (SPG) | Gen. sg (PMG) |Time Elapsed") skip = [] # slow to generate for num in range(1, 76): if num not in skip: multiplicity = len( get_rod(num)[0]) # multiplicity of the general position start = time() rand_crystal = molecular_crystal_1D(num, ["H2O"], [multiplicity], 4.0) end = time() timespent = np.around((end - start), decimals=2) t = str(timespent) if len(t) == 3: t += "0" t += " s" if timespent >= 1.0: t += " ~" if timespent >= 3.0: t += "~" if timespent >= 10.0: t += "~" if timespent >= 60.0: t += "~" slow.append(num) if rand_crystal.valid: try: ans1 = get_symmetry_dataset(rand_crystal.spg_struct, symprec=1e-1) except: ans1 = "???" if ans1 is None or ans1 == "???": ans1 = "???" else: ans1 = ans1["number"] sga = SpacegroupAnalyzer(rand_crystal.struct) try: ans2 = sga.get_space_group_number() except: ans2 = "???" if ans2 is None: ans2 = "???" check = True # output cif files for incorrect space groups if check is True: if check_struct_group(rand_crystal, num, dim=1): pass else: t += " xxxxx" outstructs.append(rand_crystal.struct) outstrings.append( str("1D_Molecular_" + str(num) + ".vasp")) print("\t" + str(num) + "\t|\t" + str(ans1) + "\t|\t" + str(ans2) + "\t|\t" + t) else: print("~~~~ Error: Could not generate layer group " + str(num) + " after " + t) failed.append(num) if slow != []: print( "~~~~ The following layer groups took more than 60 seconds to generate:" ) for i in slow: print(" " + str(i)) if failed != []: print("~~~~ The following layer groups failed to generate:") for i in failed: print(" " + str(i))
def check_struct_group(struct, group, dim=3, tol=1e-2): """Given a pymatgen structure, group number, and dimension, return whether or not the structure matches the group number.""" from pyxtal.symmetry import distance from pyxtal.symmetry import filtered_coords from copy import deepcopy lattice = struct.lattice.matrix PBC = [1,1,1] #Obtain the generators for the group if dim == 3: from pyxtal.symmetry import get_wyckoffs generators = get_wyckoffs(group)[0] elif dim == 2: from pyxtal.symmetry import get_layer generators = get_layer(group)[0] PBC = [1,1,0] elif dim == 1: from pyxtal.symmetry import get_rod generators = get_rod(group)[0] PBC = [0,0,1] elif dim == 0: from pyxtal.symmetry import get_point generators = get_point(group)[0] PBC = [0,0,0] #TODO: Add check for lattice symmetry #Apply SymmOps to generate new points #old_coords = filtered_coords(struct.frac_coords,PBC=PBC) old_coords = deepcopy(struct.frac_coords) if dim == 0: old_coords = old_coords - 0.5 old_species = deepcopy(struct.atomic_numbers) new_coords = [] new_species = [] for i, point in enumerate(old_coords): for j, op in enumerate(generators): if j != 0: new_coords.append(op.operate(point)) new_species.append(old_species[i]) #new_coords = filtered_coords(new_coords,PBC=PBC) #Check that all points in new list are still in old failed = False i_list = list(range(len(new_coords))) for i, point1 in enumerate(new_coords): found = False for j, point2 in enumerate(old_coords): if new_species[i] == old_species[j]: difference = filtered_coords(point2 - point1, PBC=PBC) if distance(difference, lattice, PBC=PBC) <= tol: found = True break if found is False: failed = True break if failed is False: return True else: return False
def test_atomic_1D(): global outstructs global outstrings fprint("=== Testing generation of atomic 1D crystals. This may take some time. ===") slow = [] failed = [] fprint(" Rod group | Gen sg. (SPG) | Gen. sg (PMG) |Time Elapsed") skip = [] # slow to generate for num in range(1, 76): if num not in skip: multiplicity = len(get_rod(num)[0]) # multiplicity of the general position start = time() rand_crystal = pyxtal() rand_crystal.from_random(1, num, ["H"], [multiplicity], 4.0) end = time() timespent = np.around((end - start), decimals=2) t = str(timespent) if len(t) == 3: t += "0" t += " s" if timespent >= 1.0: t += " ~" if timespent >= 3.0: t += "~" if timespent >= 10.0: t += "~" if timespent >= 60.0: t += "~" slow.append(num) if rand_crystal.valid: try: ans1 = get_symmetry_dataset(rand_crystal.to_ase(), symprec=1e-1) except: ans1 = "???" if ans1 is None or ans1 == "???": ans1 = "???" else: ans1 = ans1["number"] sga = SpacegroupAnalyzer(rand_crystal.to_pymatgen()) try: ans2 = sga.get_space_group_number() except: ans2 = "???" if ans2 is None: ans2 = "???" check = True # output cif files for incorrect space groups if check is True: if check_struct_group(rand_crystal, num, dim=1): pass else: t += " xxxxx" outstructs.append(rand_crystal.to_pymatgen) outstrings.append(str("1D_Atomic_" + str(num) + ".vasp")) fprint("\t{}\t|\t{}\t|\t{}\t|\t{}".format(num, ans1, ans2, t)) else: fprint( "~~~~ Error: Could not generate layer group {} after {}".format( num, t ) ) failed.append(num) if slow != []: fprint("~~~~ The following layer groups took more than 60 seconds to generate:") for i in slow: fprint(" " + str(i)) if failed != []: fprint("~~~~ The following layer groups failed to generate:") for i in failed: fprint(" " + str(i))