def from_seed(self, seed, molecule=None, tol=1e-4, relax_h=False, backend='pymatgen'): """ Load the seed structure from Pymatgen/ASE/POSCAR/CIFs Internally they will be handled by Pymatgen """ from ase import Atoms from pymatgen import Structure if self.molecular: from pyxtal.molecule import pyxtal_molecule pmol = pyxtal_molecule(molecule).mol struc = structure_from_ext(seed, pmol, relax_h=relax_h) if struc.match(): self.mol_sites = [struc.make_mol_site()] self.group = Group(struc.wyc.number) self.lattice = struc.lattice self.molecules = [ pyxtal_molecule(struc.molecule, symmetrize=False) ] self.numMols = struc.numMols self.diag = struc.diag self.valid = True # Need to add a check function else: raise ValueError( "Cannot extract the molecular crystal from cif") else: if isinstance(seed, dict): self.from_dict() elif isinstance(seed, Atoms): #ASE atoms from pymatgen.io.ase import AseAtomsAdaptor pmg_struc = AseAtomsAdaptor.get_structure(seed) self._from_pymatgen(pmg_struc, tol) elif isinstance(seed, Structure): #Pymatgen self._from_pymatgen(seed, tol) elif isinstance(seed, str): if backend == 'pymatgen': pmg_struc = Structure.from_file(seed) self._from_pymatgen(pmg_struc, tol) else: self.lattice, self.atom_sites = read_cif(seed) self.group = Group(self.atom_sites[0].wp.number) self.diag = self.atom_sites[0].diag self.valid = True self.factor = 1.0 self.source = 'Seed' self.dim = 3 self.PBC = [1, 1, 1] self._get_formula()
def from_1D_dicts(cls, dicts): from pyxtal.molecule import pyxtal_molecule, Orientation mol = pyxtal_molecule(mol=dicts['smile'] + '.smi') rdkit_mol = mol.rdkit_mol(mol.smile) conf = rdkit_mol.GetConformer(0) #print("try") #print(conf.GetPositions()[:3]) #print(dicts["rotor"]) if dicts['reflect']: mol.set_torsion_angles(conf, dicts["rotor"], False) # print(mol.set_torsion_angles(conf, dicts["rotor"], True)) # #import sys; sys.exit() xyz = mol.set_torsion_angles(conf, dicts["rotor"], dicts['reflect']) mol.reset_positions(xyz) g = dicts["number"] index = dicts["index"] dim = dicts["dim"] matrix = R.from_euler('zxy', dicts["orientation"], degrees=True).as_matrix() orientation = Orientation(matrix) #if dicts['reflect']: # print('load'); print(xyz[:3]) # print("aaaaaaaaaaaaaa"); print(xyz[:3].dot(orientation.matrix.T)) # print("matrix"); print(orientation.matrix) wp = Wyckoff_position.from_group_and_index(g, index, dim) diag = dicts["diag"] lattice = Lattice.from_matrix(dicts["lattice"], ltype=dicts["lattice_type"]) position = dicts[ "center"] #np.dot(dicts["center"], lattice.inv_matrix) return cls(mol, position, orientation, wp, lattice, diag)
def make_mol_site(self, ref=False): if ref: mol = self.ref_mol ori = self.ori else: mol = self.molecule ori = Orientation(np.eye(3)) mol = self.add_site_props(mol) pmol = pyxtal_molecule(mol, symmetrize=False) site = mol_site(pmol, self.position, ori, self.wyc, self.lattice, self.diag) return site
def __init__(self, struc, ref_mols, tol=0.2, relax_h=False): """ extract the mol_site information from the give cif file and reference molecule Args: struc: cif/poscar file or a Pymatgen Structure object ref_mols: a list of reference molecule (xyz file or Pyxtal molecule) tol: scale factor for covalent bond distance relax_h: whether or not relax the position for hydrogen atoms in structure """ for ref_mol in ref_mols: if isinstance(ref_mol, str): ref_mol = pyxtal_molecule(ref_mol) elif isinstance(ref_mol, pyxtal_molecule): ref_mol = ref_mol else: print(type(ref_mol)) raise NameError("reference molecule cannot be defined") if isinstance(struc, str): pmg_struc = Structure.from_file(struc) elif isinstance(struc, Structure): pmg_struc = struc else: print(type(struc)) raise NameError("input structure cannot be intepretted") self.ref_mols = ref_mols self.tol = tol self.diag = False self.relax_h = relax_h sym_struc, number = get_symmetrized_pmg(pmg_struc) group = Group(number) self.group = group self.wyc = group[0] self.perm = [0,1,2] molecules = search_molecules_in_crystal(sym_struc, self.tol) if self.relax_h: molecules = self.addh(molecules) self.pmg_struc = sym_struc self.lattice = Lattice.from_matrix(sym_struc.lattice.matrix, ltype=group.lattice_type) self.resort(molecules) self.numMols = [len(self.wyc)]
def init_common( self, molecules, numMols, volume_factor, select_high, allow_inversion, orientations, group, lattice, tm, sites, ): # init functionality which is shared by 3D, 2D, and 1D crystals self.valid = False self.numattempts = 0 # number of attempts to generate the crystal. if type(group) == Group: self.group = group """A pyxtal.symmetry.Group object storing information about the space/layer /Rod/point group, and its Wyckoff positions.""" else: self.group = Group(group, dim=self.dim) self.number = self.group.number """ The international group number of the crystal: 1-230 for 3D space groups 1-80 for 2D layer groups 1-75 for 1D Rod groups 1-32 for crystallographic point groups None otherwise """ self.Msgs() self.factor = volume_factor # volume factor for the unit cell. numMols = np.array(numMols) # must convert it to np.array self.numMols0 = numMols # in the PRIMITIVE cell self.numMols = self.numMols0 * cellsize( self.group) # in the CONVENTIONAL cell # boolean numbers self.allow_inversion = allow_inversion self.select_high = select_high # Set the tolerance matrix # The Tol_matrix object for checking inter-atomic distances within the structure. if type(tm) == Tol_matrix: self.tol_matrix = tm else: try: self.tol_matrix = Tol_matrix(prototype=tm) # TODO remove bare except except: msg = "Error: tm must either be a Tol_matrix object +\n" msg += "or a prototype string for initializing one." printx(msg, priority=1) return self.molecules = [] # A pyxtal_molecule objects, for mol in molecules: self.molecules.append(pyxtal_molecule(mol, self.tol_matrix)) self.sites = {} for i, mol in enumerate(self.molecules): if sites is not None and sites[i] is not None: self.check_consistency(sites[i], self.numMols[i]) self.sites[i] = sites[i] else: self.sites[i] = None # if seeds, directly parse the structure from cif # At the moment, we only support one specie if self.seed is not None: seed = structure_from_ext(self.seed, self.molecules[0].mol, relax_h=self.relax_h) if seed.match(): self.mol_sites = [seed.make_mol_site()] self.group = Group(seed.wyc.number) self.lattice = seed.lattice self.molecules = [pyxtal_molecule(seed.molecule)] self.diag = seed.diag self.valid = True # Need to add a check function else: raise ValueError("Cannot extract the structure from cif") # The valid orientations for each molecule and Wyckoff position. # May be copied when generating a new molecular_crystal to save a # small amount of time if orientations is None: self.get_orientations() else: self.valid_orientations = orientations if self.seed is None: if lattice is not None: # Use the provided lattice self.lattice = lattice self.volume = lattice.volume # Make sure the custom lattice PBC axes are correct. if lattice.PBC != self.PBC: self.lattice.PBC = self.PBC printx("\n Warning: converting custom lattice PBC to " + str(self.PBC)) else: # Determine the unique axis if self.dim == 2: if self.number in range(3, 8): unique_axis = "c" else: unique_axis = "a" elif self.dim == 1: if self.number in range(3, 8): unique_axis = "a" else: unique_axis = "c" else: unique_axis = "c" # Generate a Lattice instance self.volume = self.estimate_volume() # The Lattice object used to generate lattice matrices if self.dim == 3 or self.dim == 0: self.lattice = Lattice( self.group.lattice_type, self.volume, PBC=self.PBC, unique_axis=unique_axis, ) elif self.dim == 2: self.lattice = Lattice( self.group.lattice_type, self.volume, PBC=self.PBC, unique_axis=unique_axis, thickness=self.thickness, ) elif self.dim == 1: self.lattice = Lattice( self.group.lattice_type, self.volume, PBC=self.PBC, unique_axis=unique_axis, area=self.area, ) self.generate_crystal()
def init_common( self, molecules, numMols, volume_factor, select_high, allow_inversion, orientations, group, lattice, sites, conventional, tm, ): # init functionality which is shared by 3D, 2D, and 1D crystals self.valid = False self.numattempts = 0 # number of attempts to generate the crystal. if type(group) == Group: self.group = group else: self.group = Group(group, dim=self.dim) self.number = self.group.number """ The international group number of the crystal: 1-230 for 3D space groups 1-80 for 2D layer groups 1-75 for 1D Rod groups 1-32 for crystallographic point groups None otherwise """ self.factor = volume_factor # volume factor for the unit cell. numMols = np.array(numMols) # must convert it to np.array if not conventional: mul = cellsize(self.group) else: mul = 1 self.numMols = numMols * mul # boolean numbers self.allow_inversion = allow_inversion self.select_high = select_high # Set the tolerance matrix # The Tol_matrix object for checking inter-atomic distances within the structure. if type(tm) == Tol_matrix: self.tol_matrix = tm else: try: self.tol_matrix = Tol_matrix(prototype=tm) # TODO remove bare except except: msg = "Error: tm must either be a Tol_matrix object +\n" msg += "or a prototype string for initializing one." printx(msg, priority=1) return self.molecules = [] # A pyxtal_molecule objects, for mol in molecules: self.molecules.append(pyxtal_molecule(mol, self.tol_matrix)) self.sites = {} for i, mol in enumerate(self.molecules): if sites is not None and sites[i] is not None: self.check_consistency(sites[i], self.numMols[i]) self.sites[i] = sites[i] else: self.sites[i] = None # The valid orientations for each molecule and Wyckoff position. # May be copied when generating a new molecular_crystal to save a # small amount of time if orientations is None: self.get_orientations() else: self.valid_orientations = orientations if lattice is not None: # Use the provided lattice self.lattice = lattice self.volume = lattice.volume # Make sure the custom lattice PBC axes are correct. if lattice.PBC != self.PBC: self.lattice.PBC = self.PBC printx("\n Warning: converting custom lattice PBC to " + str(self.PBC)) else: # Determine the unique axis if self.dim == 2: if self.number in range(3, 8): unique_axis = "c" else: unique_axis = "a" elif self.dim == 1: if self.number in range(3, 8): unique_axis = "a" else: unique_axis = "c" else: unique_axis = "c" # Generate a Lattice instance self.volume = self.estimate_volume() # The Lattice object used to generate lattice matrices if self.dim == 3 or self.dim == 0: self.lattice = Lattice( self.group.lattice_type, self.volume, PBC=self.PBC, unique_axis=unique_axis, ) elif self.dim == 2: self.lattice = Lattice( self.group.lattice_type, self.volume, PBC=self.PBC, unique_axis=unique_axis, thickness=self.thickness, ) elif self.dim == 1: self.lattice = Lattice( self.group.lattice_type, self.volume, PBC=self.PBC, unique_axis=unique_axis, area=self.area, ) self.generate_crystal()
def test_modules(): fprint("====== Testing functionality for pyXtal version 0.1dev ======") global failed_package failed_package = False # Record if errors occur at any level reset() fprint("Importing sys...") try: import sys fprint("Success!") except Exception as e: fail(e) sys.exit(0) fprint("Importing numpy...") try: import numpy as np fprint("Success!") except Exception as e: fail(e) sys.exit(0) fprint("Importing pymatgen...") try: import pymatgen fprint("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) fprint("Importing pandas...") try: import pandas fprint("Success!") except Exception as e: fail(e) sys.exit(0) fprint("Importing spglib...") try: import spglib fprint("Success!") except Exception as e: fail(e) sys.exit(0) fprint("Importing ase...") try: import ase fprint("Success!") except: fprint("Error: could not import openbabel. Try reinstalling the package.") fprint("=== Testing modules ===") # =====database.element===== fprint("pyxtal.database.element") reset() try: import pyxtal.database.element except Exception as e: fail(e) fprint(" 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===== fprint("pyxtal.database.hall") reset() try: import pyxtal.database.hall except Exception as e: fail(e) fprint(" 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===== fprint("pyxtal.database.collection") reset() try: import pyxtal.database.collection except Exception as e: fail(e) fprint(" 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===== fprint("pyxtal.operations") reset() try: import pyxtal.operations except Exception as e: fail(e) from pyxtal.lattice import random_shear_matrix, random_vector fprint(" 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() fprint(" 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() fprint(" 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() fprint(" 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() fprint(" class OperationAnalyzer") try: from pyxtal.operations import OperationAnalyzer except Exception as e: fail(e) if passed(): try: m = np.eye(3) t = random_vector() op1 = SymmOp.from_rotation_and_translation(m, t) OperationAnalyzer(op1) except Exception as e: fail(e) check() fprint(" class Orientation") try: from pyxtal.molecule 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===== fprint("pyxtal.symmetry") reset() try: import pyxtal.symmetry except Exception as e: fail(e) fprint(" 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() fprint(" 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() fprint(" 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() fprint(" 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() fprint(" 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() fprint(" 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: fprint(j, k) fail() except Exception as e: fail(e) check() fprint(" 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: fprint(j, k) fail() except Exception as e: fail(e) check() fprint(" 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() fprint(" 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() fprint(" 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() # =====molecule===== fprint("pyxtal.molecule") reset() try: from pyxtal.molecule import pyxtal_molecule except Exception as e: fail(e) if passed(): try: h2o = pyxtal_molecule("H2O").mol ch4 = pyxtal_molecule("CH4").mol except Exception as e: fail(e) check() fprint(" 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() fprint(" 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() end(condition=2)