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_modules(): print("====== Testing functionality for pyXtal version 0.1dev ======") global failed_package failed_package = False # Record if errors occur at any level reset() print("Importing sys...") try: import sys print("Success!") except Exception as e: fail(e) sys.exit(0) print("Importing numpy...") try: import numpy as np print("Success!") except Exception as e: fail(e) sys.exit(0) I = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) print("Importing pymatgen...") try: import pymatgen print("Success!") except Exception as e: fail(e) sys.exit(0) try: from pymatgen.core.operations import SymmOp except Exception as e: fail(e) sys.exit(0) print("Importing pandas...") try: import pandas print("Success!") except Exception as e: fail(e) sys.exit(0) print("Importing spglib...") try: import spglib print("Success!") except Exception as e: fail(e) sys.exit(0) print("Importing openbabel...") try: import ase print("Success!") except: print( "Error: could not import openbabel. Try reinstalling the package.") print("Importing pyxtal...") try: import pyxtal print("Success!") except Exception as e: fail(e) sys.exit(0) print("=== Testing modules ===") # =====database.element===== print("pyxtal.database.element") reset() try: import pyxtal.database.element except Exception as e: fail(e) print(" class Element") try: from pyxtal.database.element import Element except Exception as e: fail(e) if passed(): for i in range(1, 95): if passed(): try: ele = Element(i) except: fail("Could not access Element # " + str(i)) try: y = ele.sf y = ele.z y = ele.short_name y = ele.long_name y = ele.valence y = ele.valence_electrons y = ele.covalent_radius y = ele.vdw_radius y = ele.get_all(0) except: fail("Could not access attribute for element # " + str(i)) try: ele.all_z() ele.all_short_names() ele.all_long_names() ele.all_valences() ele.all_valence_electrons() ele.all_covalent_radii() ele.all_vdw_radii() except: fail("Could not access class methods") check() # =====database.hall===== print("pyxtal.database.hall") reset() try: import pyxtal.database.hall except Exception as e: fail(e) print(" hall_from_hm") try: from pyxtal.database.hall import hall_from_hm except Exception as e: fail(e) if passed(): for i in range(1, 230): if passed(): try: hall_from_hm(i) except: fail("Could not access hm # " + str(i)) check() # =====database.collection===== print("pyxtal.database.collection") reset() try: import pyxtal.database.collection except Exception as e: fail(e) print(" Collection") try: from pyxtal.database.collection import Collection except Exception as e: fail(e) if passed(): for i in range(1, 230): if passed(): try: molecule_collection = Collection("molecules") except: fail("Could not access hm # " + str(i)) check() # =====operations===== print("pyxtal.operations") reset() try: import pyxtal.operations except Exception as e: fail(e) print(" random_vector") try: from pyxtal.operations import random_vector except Exception as e: fail(e) if passed(): try: for i in range(10): random_vector() except Exception as e: fail(e) check() print(" angle") try: from pyxtal.operations import angle except Exception as e: fail(e) if passed(): try: for i in range(10): v1 = random_vector() v2 = random_vector() angle(v1, v2) except Exception as e: fail(e) check() print(" random_shear_matrix") try: from pyxtal.operations import random_shear_matrix except Exception as e: fail(e) if passed(): try: for i in range(10): random_shear_matrix() except Exception as e: fail(e) check() print(" is_orthogonal") try: from pyxtal.operations import is_orthogonal except Exception as e: fail(e) if passed(): try: a = is_orthogonal([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) b = is_orthogonal([[0, 0, 1], [1, 0, 0], [1, 0, 0]]) if a is True and b is False: pass else: fail() except Exception as e: fail(e) check() print(" aa2matrix") try: from pyxtal.operations import aa2matrix except Exception as e: fail(e) if passed(): try: for i in range(10): aa2matrix(1, 1, random=True) except Exception as e: fail(e) check() print(" matrix2aa") try: from pyxtal.operations import matrix2aa except Exception as e: fail(e) if passed(): try: for i in range(10): m = aa2matrix(1, 1, random=True) aa = matrix2aa(m) except Exception as e: fail(e) check() print(" rotate_vector") try: from pyxtal.operations import rotate_vector except Exception as e: fail(e) if passed(): try: for i in range(10): v1 = random_vector() v2 = random_vector() rotate_vector(v1, v2) except Exception as e: fail(e) check() print(" are_equal") try: from pyxtal.operations import are_equal except Exception as e: fail(e) if passed(): try: op1 = SymmOp.from_xyz_string("x,y,z") op2 = SymmOp.from_xyz_string("x,y,z+1") a = are_equal(op1, op2, PBC=[0, 0, 1]) b = are_equal(op1, op2, PBC=[1, 0, 0]) if a is True and b is False: pass else: fail() except Exception as e: fail(e) check() print(" class OperationAnalyzer") try: from pyxtal.operations import OperationAnalyzer except Exception as e: fail(e) if passed(): try: for i in range(10): m = aa2matrix(1, 1, random=True) t = random_vector() op1 = SymmOp.from_rotation_and_translation(m, t) OperationAnalyzer(op1) except Exception as e: fail(e) check() print(" class Orientation") try: from pyxtal.operations import Orientation except Exception as e: fail(e) if passed(): try: for i in range(10): v1 = random_vector() c1 = random_vector() o = Orientation.from_constraint(v1, c1) except Exception as e: fail(e) check() # =====symmetry===== print("pyxtal.symmetry") reset() try: import pyxtal.symmetry except Exception as e: fail(e) print(" get_wyckoffs (may take a moment)") try: from pyxtal.symmetry import get_wyckoffs except Exception as e: fail(e) if passed(): try: for i in [1, 2, 229, 230]: get_wyckoffs(i) get_wyckoffs(i, organized=True) except: fail(" Could not access Wyckoff positions for space group # " + str(i)) check() print(" get_wyckoff_symmetry (may take a moment)") try: from pyxtal.symmetry import get_wyckoff_symmetry except Exception as e: fail(e) if passed(): try: for i in [1, 2, 229, 230]: get_wyckoff_symmetry(i) get_wyckoff_symmetry(i, molecular=True) except: fail("Could not access Wyckoff symmetry for space group # " + str(i)) check() print(" get_wyckoffs_generators (may take a moment)") try: from pyxtal.symmetry import get_wyckoff_generators except Exception as e: fail(e) if passed(): try: for i in [1, 2, 229, 230]: get_wyckoff_generators(i) except: fail("Could not access Wyckoff generators for space group # " + str(i)) check() print(" letter_from_index") try: from pyxtal.symmetry import letter_from_index except Exception as e: fail(e) if passed(): try: if letter_from_index(0, get_wyckoffs(47)) == "A": pass else: fail() except Exception as e: fail(e) check() print(" index_from_letter") try: from pyxtal.symmetry import index_from_letter except Exception as e: fail(e) if passed(): try: if index_from_letter("A", get_wyckoffs(47)) == 0: pass else: fail() except Exception as e: fail(e) check() print(" jk_from_i") try: from pyxtal.symmetry import jk_from_i except Exception as e: fail(e) if passed(): try: w = get_wyckoffs(2, organized=True) j, k = jk_from_i(1, w) if j == 1 and k == 0: pass else: print(j, k) fail() except Exception as e: fail(e) check() print(" i_from_jk") try: from pyxtal.symmetry import i_from_jk except Exception as e: fail(e) if passed(): try: w = get_wyckoffs(2, organized=True) j, k = jk_from_i(1, w) i = i_from_jk(j, k, w) if i == 1: pass else: print(j, k) fail() except Exception as e: fail(e) check() print(" ss_string_from_ops") try: from pyxtal.symmetry import ss_string_from_ops except Exception as e: fail(e) if passed(): try: strings = ["1", "4 . .", "2 3 ."] for i, sg in enumerate([1, 75, 195]): ops = get_wyckoffs(sg)[0] ss_string_from_ops(ops, sg, dim=3) except Exception as e: fail(e) check() print(" Wyckoff_position") try: from pyxtal.symmetry import Wyckoff_position except Exception as e: fail(e) if passed(): try: wp = Wyckoff_position.from_group_and_index(20, 1) except Exception as e: fail(e) check() print(" Group") try: from pyxtal.symmetry import Group except Exception as e: fail(e) if passed(): try: g3 = Group(230) g2 = Group(80, dim=2) g1 = Group(75, dim=1) except Exception as e: fail(e) check() # =====crystal===== print("pyxtal.crystal") reset() try: import pyxtal.crystal except Exception as e: fail(e) print(" random_crystal") try: from pyxtal.crystal import random_crystal except Exception as e: fail(e) if passed(): try: c = random_crystal(1, ["H"], [1], 10.0) if c.valid is True: pass else: fail() except Exception as e: fail(e) check() print(" random_crystal_2D") try: from pyxtal.crystal import random_crystal_2D except Exception as e: fail(e) if passed(): try: c = random_crystal_2D(1, ["H"], [1], 10.0) if c.valid is True: pass else: fail() except Exception as e: fail(e) check() # =====molecule===== print("pyxtal.molecule") reset() try: import pyxtal.molecule except Exception as e: fail(e) check() print(" Collections") try: from pyxtal.molecule import mol_from_collection except Exception as e: fail(e) if passed(): try: h2o = mol_from_collection("H2O") ch4 = mol_from_collection("CH4") except Exception as e: fail(e) print(" get_inertia_tensor") try: from pyxtal.molecule import get_inertia_tensor except Exception as e: fail(e) if passed(): try: get_inertia_tensor(h2o) get_inertia_tensor(ch4) except Exception as e: fail(e) check() print(" get_moment_of_inertia") try: from pyxtal.molecule import get_moment_of_inertia except Exception as e: fail(e) if passed(): try: v = random_vector() get_moment_of_inertia(h2o, v) get_moment_of_inertia(ch4, v) except Exception as e: fail(e) check() print(" reoriented_molecule") try: from pyxtal.molecule import reoriented_molecule except Exception as e: fail(e) if passed(): try: reoriented_molecule(h2o) reoriented_molecule(ch4) except Exception as e: fail(e) check() print(" orientation_in_wyckoff_position") try: from pyxtal.molecule import orientation_in_wyckoff_position except Exception as e: fail(e) if passed(): try: w = get_wyckoffs(20) ws = get_wyckoff_symmetry(20, molecular=True) wp = Wyckoff_position.from_group_and_index(20, 1) orientation_in_wyckoff_position(h2o, wp) orientation_in_wyckoff_position(ch4, wp) except Exception as e: fail(e) check() # =====molecular_crystal===== print("pyxtal.molecular_crystal") reset() try: import pyxtal.crystal except Exception as e: fail(e) print(" molecular_crystal") try: from pyxtal.molecular_crystal import molecular_crystal except Exception as e: fail(e) if passed(): try: c = molecular_crystal(1, ["H2O"], [1], 10.0) if c.valid is True: pass else: fail() except Exception as e: fail(e) check() print(" molecular_crystal_2D") try: from pyxtal.molecular_crystal import molecular_crystal_2D except Exception as e: fail(e) if passed(): try: c = molecular_crystal_2D(1, ["H2O"], [1], 10.0) if c.valid is True: pass else: fail() except Exception as e: fail(e) check() end(condition=2)
def test_molecular(): global outstructs global outstrings print( "=== Testing generation of molecular 3D crystals. This may take some time. ===" ) from time import time from spglib import get_symmetry_dataset from pyxtal.symmetry import get_wyckoffs from pyxtal.crystal import cellsize from pyxtal.molecular_crystal import molecular_crystal from pymatgen.symmetry.analyzer import SpacegroupAnalyzer slow = [] failed = [] print(" Spacegroup # |Generated (SPG)|Generated (PMG)| Time Elapsed") skip = ( [] ) # [24, 183, 202, 203, 209, 210, 216, 219, 225, 226, 227, 228, 229, 230] #slow for sg in range(1, 231): if sg not in skip: multiplicity = len(get_wyckoffs(sg)[0]) / cellsize( sg) # multiplicity of the general position start = time() rand_crystal = molecular_crystal(sg, ["H2O"], [multiplicity], 2.5) 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(sg) if rand_crystal.valid: check = False ans1 = get_symmetry_dataset(rand_crystal.spg_struct, symprec=1e-1) if ans1 is None: ans1 = "???" else: ans1 = ans1["number"] sga = SpacegroupAnalyzer(rand_crystal.struct) ans2 = "???" if sga is not None: try: ans2 = sga.get_space_group_number() except: ans2 = "???" if ans2 is None: ans2 = "???" # Compare expected and detected groups if ans1 == "???" and ans2 == "???": check = True elif ans1 == "???": if int(ans2) > sg: pass elif ans2 == "???": if int(ans1) > sg: pass else: if ans1 < sg and ans2 < sg: if compare_wyckoffs(sg, ans1) or compare_wyckoffs( sg, ans2): pass else: check = True # output cif files for incorrect space groups if check is True: if check_struct_group(rand_crystal, sg, dim=3): pass else: t += " xxxxx" outstructs.append(rand_crystal.struct) outstrings.append( str("3D_Molecular_" + str(sg) + ".vasp")) print("\t" + str(sg) + "\t|\t" + str(ans1) + "\t|\t" + str(ans2) + "\t|\t" + t) else: print("~~~~ Error: Could not generate space group " + str(sg) + " after " + t) failed.append(sg) if slow != []: print( "~~~~ The following space groups took more than 60 seconds to generate:" ) for i in slow: print(" " + str(i)) if failed != []: print("~~~~ The following space 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_get_wyckoff(self): for i in [1, 2, 229, 230]: get_wyckoffs(i) get_wyckoffs(i, organized=True)
def test_molecular(): global outstructs global outstrings fprint( "=== Testing generation of molecular 3D crystals. This may take some time. ===" ) slow = [] failed = [] fprint(" Spacegroup # |Generated (SPG)|Generated (PMG)| Time Elapsed") skip = [ 225, 226, 227, 228, ] # [24, 183, 202, 203, 209, 210, 216, 219, 225, 226, 227, 228, 229, 230] #slow for sg in range(1, 231): if sg not in skip: multiplicity = len(get_wyckoffs(sg)[0]) start = time() rand_crystal = pyxtal(molecular=True) rand_crystal.from_random(3, sg, ["H2O"], [multiplicity], 2.5) 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(sg) if rand_crystal.valid: check = False ans1 = get_symmetry_dataset(rand_crystal.to_ase(), symprec=1e-1) if ans1 is None: ans1 = "???" else: ans1 = ans1["number"] sga = SpacegroupAnalyzer(rand_crystal.to_pymatgen()) ans2 = "???" if sga is not None: try: ans2 = sga.get_space_group_number() except: ans2 = "???" if ans2 is None: ans2 = "???" # Compare expected and detected groups if ans1 == "???" and ans2 == "???": check = True elif ans1 == "???": if int(ans2) > sg: pass elif ans2 == "???": if int(ans1) > sg: pass else: if ans1 < sg and ans2 < sg: if compare_wyckoffs(sg, ans1) or compare_wyckoffs(sg, ans2): pass else: check = True # output cif files for incorrect space groups if check is True: if check_struct_group(rand_crystal, sg, dim=3): pass else: t += " xxxxx" # rand_crystal.to_file("poscar", "1.vasp") # import sys # sys.exit() outstructs.append(rand_crystal.to_pymatgen()) outstrings.append(str("3D_Molecular_" + str(sg) + ".vasp")) fprint("\t{}\t|\t{}\t|\t{}\t|\t{}".format(sg, ans1, ans2, t)) else: fprint( "~~~~ Error: Could not generate space group {} after {}".format(sg, t) ) failed.append(sg) if slow != []: fprint("~~~~ The following space groups took more than 60 seconds to generate:") for i in slow: fprint(" " + str(i)) if failed != []: fprint("~~~~ The following space groups failed to generate:") for i in failed: fprint(" " + str(i))
def test_atomic(): global outstructs global outstrings fprint("=== Testing generation of atomic 3D crystals. This may take some time. ===") my_crystal1 = pyxtal() my_crystal1.from_random(3, 99, ['Ba','Ti','O'], [1,1,3], 1.0, sites=[["1b"], ["1b"], ["2c", "1b"]]) print(my_crystal1) #my_crystal1.to_file("1.cif") my_crystal2 = pyxtal() my_crystal2.from_random(3, 225, ['C'], [12], 1.0, sites=[["4a", "8c"]]) print(my_crystal2) #my_crystal2.to_file("2.cif") my_crystal3 = pyxtal() my_crystal3.from_random(3, 225, ['C','Si'], [12, 4], 1.0, sites=[["4a", "8c"], None]) print(my_crystal3) #my_crystal3.to_file("3.cif") slow = [] failed = [] fprint(" Spacegroup # |Generated (SPG)|Generated (PMG)| Time Elapsed") skip = [] # skip = ( # [124, 139, 166, 167, 196, 202, 203, 204, 207, 209, 210, 216, 217, # 219, 220, 221, 223, 225, 226, 227, 228, 229, 230] #slow to generate # ) for sg in range(1, 231): if sg not in skip: multiplicity = len(get_wyckoffs(sg)[0]) start = time() rand_crystal = pyxtal() rand_crystal.from_random(3, sg, ["C"], [multiplicity], 1.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(sg) if rand_crystal.valid: check = False #ans1 = get_symmetry_dataset(rand_crystal.spg_struct, symprec=1e-1) ans1 = get_symmetry_dataset(rand_crystal.to_ase(), symprec=1e-1) if ans1 is None: ans1 = "???" else: ans1 = ans1["number"] sga = SpacegroupAnalyzer(rand_crystal.to_pymatgen()) ans2 = "???" if sga is not None: try: ans2 = sga.get_space_group_number() except: ans2 = "???" if ans2 is None: ans2 = "???" # Compare expected and detected groups if ans1 == "???" and ans2 == "???": check = True elif ans1 == "???": if int(ans2) > sg: pass elif ans2 == "???": if int(ans1) > sg: pass else: if ans1 < sg and ans2 < sg: if compare_wyckoffs(sg, ans1) or compare_wyckoffs(sg, ans2): pass else: check = True # output cif files for incorrect space groups if check is True: if check_struct_group(rand_crystal, sg, dim=3): pass else: t += " xxxxx" outstructs.append(rand_crystal.struct) outstrings.append(str("3D_Atomic_" + str(sg) + ".vasp")) fprint("\t{}\t|\t{}\t|\t{}\t|\t{}".format(sg, ans1, ans2, t)) else: fprint( "~~~~ Error: Could not generate space group {} after {}".format(sg, t) ) failed.append(sg) if slow != []: fprint("~~~~ The following space groups took more than 60 seconds to generate:") for i in slow: fprint(" " + str(i)) if failed != []: fprint("~~~~ The following space groups failed to generate:") for i in failed: fprint(" " + str(i))