def __init__(self, wp, coordinate, specie=1): self.position = np.array(coordinate) self.specie = Element(specie).short_name self.multiplicity = wp.multiplicity self.wp = wp self.PBC = wp.PBC self.update(coordinate)
def get_box(self): """ Given a molecule, find a minimum orthorhombic box containing it. Size is calculated using min and max x, y, and z values, plus the padding defined by the vdw radius For best results, call oriented_molecule first. Args: mol: a pymatgen Molecule object. Should be oriented along its principle axes. Returns: a Box object """ mol, P = reoriented_molecule(self.mol) minx, miny, minz, maxx, maxy, maxz = 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 for p in mol: x, y, z = p.coords r = Element(p.species_string).vdw_radius if x - r < minx: minx = x - r if y - r < miny: miny = y - r if z - r < minz: minz = z - r if x + r > maxx: maxx = x + r if y + r > maxy: maxy = y + r if z + r > maxz: maxz = z + r self.box = Box(minx, maxx, miny, maxy, minz, maxz) self.axes = P
def estimate_volume(self): """ Estimates the volume of a unit cell based on the number and types of ions. Assumes each atom takes up a sphere with radius equal to its covalent bond radius. Returns: a float value for the estimated volume """ volume = 0 for numIon, specie in zip(self.numIons, self.species): r = random.uniform( Element(specie).covalent_radius, Element(specie).vdw_radius) volume += numIon * 4 / 3 * np.pi * r**3 return self.factor * volume
def get_tol(self, specie1, specie2): """ Returns the tolerance between two species. Args: specie1, specie2: the atomic number (int or float), name (str), symbol (str), an Element object, or a pymatgen Specie object Returns: the tolerance between the provided pair of atomic species """ if self.prototype == "single_value": return self.matrix[0][0] index1 = Element.number_from_specie(specie1) index2 = Element.number_from_specie(specie2) if index1 is not None and index2 is not None: return self.matrix[index1][index2] else: return None
def print_all(self): print("--Tol_matrix class object--") print(" Prototype: " + str(self.prototype)) print(" Atomic radius type: " + str(self.radius_type)) print(" Radius scaling factor: " + str(self.f)) if self.prototype == "single value": print(" Custom tolerance value: " + str(self.matrix([0][0]))) else: if self.custom_values == []: print(" Custom tolerance values: None") else: print(" Custom tolerance values:") for tup in self.custom_values: name1 = str(Element(tup[0]).short_name) name2 = str(Element(tup[1]).short_name) # print(" " + name1+ ", " + name2 + ": " +str(self.get_tol(tup[0],tup[1]))) s += "\n{:s}-{:s}: {:6.3f}".format( name1, name2, self.get_tol(tup[0], tup[1])) print(s)
def set_tol(self, specie1, specie2, value): """ Sets the distance tolerance between two species. Args: specie1, specie2: the atomic number (int or float), name (str), symbol (str), an Element object, or a pymatgen Specie object value: the tolerance (in Angstroms) to set to """ index1 = Element.number_from_specie(specie1) index2 = Element.number_from_specie(specie2) if index1 is None or index2 is None: return self.matrix[index1][index2] = float(value) if index1 != index2: self.matrix[index2][index1] = float(value) if (index1, index2) not in self.custom_values and ( index2, index1, ) not in self.custom_values: larger = max(index1, index2) smaller = min(index1, index2) self.custom_values.append((smaller, larger))
def atom_scatter(self, crystal): """ N*M array; N: atoms, M: N_hkl""" f = np.zeros([sum(crystal.composition), len(self.d_hkl)]) d0 = 1/2/self.d_hkl count = 0 for i, ele in enumerate(crystal.atom_type): c = Element(ele).scatter f_tmp = c[0]*np.exp(-c[4]*d0) + \ c[1]*np.exp(-c[5]*d0) + \ c[2]*np.exp(-c[6]*d0) + \ c[3]*np.exp(-c[7]*d0) + c[8] for j in range(count,count+crystal.composition[i]): f[j] = f_tmp count += crystal.composition[i] self.f = f
def read_molecule(mol, name): x = np.transpose([mol.record["coords"][0]["conformers"][0]["x"]]) y = np.transpose([mol.record["coords"][0]["conformers"][0]["y"]]) z = np.transpose([mol.record["coords"][0]["conformers"][0]["z"]]) xyz = np.concatenate((x, y, z), axis=1) numbers = mol.record["atoms"]["element"] elements = [Element(i).short_name for i in numbers] volume = mol.volume_3d pubchemid = mol.cid molecule = { "name": name, "elements": elements, "xyz": xyz, "volume": volume, "pubchem id": pubchemid, } return molecule
def __init__(self, wp=None, coordinate=None, specie=1, diag=False, search=False): self.position = np.array(coordinate) self.specie = Element(specie).short_name self.diag = diag self.wp = wp if self.diag: self.wp.diagonalize_symops() #self.position = project_point(self.position, wp[0]) self._get_dof() self.PBC = self.wp.PBC self.multiplicity = self.wp.multiplicity if search: self.search_position() self.update()
def intensity(self, crystal): """ This function calculates all that is necessary to find the intensities. This scheme is based off of pymatgen Needs improvement from different correction factors. """ d0 = (1 / 2 / self.d_hkl)**2 # obtiain scattering parameters, atomic numbers, and occus (need to look into occus) coeffs = [] zs = [] for elem in crystal.get_chemical_symbols(): if elem == 'D': elem = 'H' c = ATOMIC_SCATTERING_PARAMS[elem] z = Element(elem).z coeffs.append(c) zs.append(z) coeffs = np.array(coeffs) self.peaks = {} two_thetas = [] # self.march_parameter = 1 TWO_THETA_TOL = 1e-5 # tolerance to find repeating angles SCALED_INTENSITY_TOL = 1e-5 # threshold for intensities for hkl, s2, theta, d_hkl in zip(self.hkl_list, d0, self.theta, self.d_hkl): # calculate the scattering factor sf g_dot_r = np.dot(crystal.get_scaled_positions(), np.transpose([hkl])).T[0] sf = zs - 41.78214 * s2 * np.sum( coeffs[:, :, 0] * np.exp(-coeffs[:, :, 1] * s2), axis=1) # calculate the structure factor f f = np.sum(sf * np.exp(2j * np.pi * g_dot_r)) # calculate the lorentz polarization factor lf lf = (1 + np.cos(2 * theta)**2) / (np.sin(theta)**2 * np.cos(theta)) # calculate the preferred orientation factor if self.preferred_orientation != False: G = self.march_parameter po = ((G * np.cos(theta))**2 + 1 / G * np.sin(theta)**2)**(-3 / 2) else: po = 1 # calculate the intensity I I = (f * f.conjugate()).real # calculate 2*theta two_theta = np.degrees(2 * theta) # find where the scattered angles are equal ind = np.where( np.abs(np.subtract(two_thetas, two_theta)) < TWO_THETA_TOL) # append intensity, hkl plane, and thetas to lists if len(ind[0]) > 0: self.peaks[two_thetas[ind[0][0]]][0] += I * lf * po self.peaks[two_thetas[ind[0][0]]][1].append(tuple(hkl)) else: self.peaks[two_theta] = [I * lf * po, [tuple(hkl)], d_hkl] two_thetas.append(two_theta) # obtain important intensities (defined by SCALED_INTENSITY_TOL) # and corresponding 2*theta, hkl plane + multiplicity, and d_hkl max_intensity = max([v[0] for v in self.peaks.values()]) x = [] y = [] hkls = [] d_hkls = [] count = 0 for k in sorted(self.peaks.keys()): count += 1 v = self.peaks[k] fam = self.get_unique_families(v[1]) if v[0] / max_intensity * 100 > SCALED_INTENSITY_TOL: x.append(k) y.append(v[0]) hkls.append([{ "hkl": hkl, "multiplicity": mult } for hkl, mult in fam.items()]) d_hkls.append(v[2]) self.theta2 = x self.xrd_intensity = y self.hkl_labels = hkls self.d_hkls = d_hkls
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 __getitem__(self, index): new_index = Element.number_from_specie(index) return self.matrix[index]
def __init__(self, *tuples, prototype="atomic", factor=1.0): f = factor self.prototype = prototype if prototype == "atomic": f *= 0.5 attrindex = 5 self.radius_type = "covalent" elif prototype == "molecular": attrindex = 5 self.radius_type = "covalent" f *= 1.2 elif prototype == "metallic": attrindex = 7 self.radius_type = "metallic" f *= 0.5 else: self.radius_type = "N/A" self.f = f H = Element("H") m = [[0.0] * (len(H.elements_list) + 1)] for i, tup1 in enumerate(H.elements_list): m.append([0.0]) for j, tup2 in enumerate(H.elements_list): # Get the appropriate atomic radii if tup1[attrindex] is None: if tup1[5] is None: val1 = None else: # Use the covalent radius val1 = tup1[5] else: val1 = tup1[attrindex] if tup2[attrindex] is None: if tup2[5] is None: val2 = None else: # Use the covalent radius val2 = tup1[5] else: val2 = tup2[attrindex] if val1 is not None and val2 is not None: m[-1].append(f * (val1 + val2)) else: # If no radius is found for either atom, set tolerance to None m[-1].append(None) self.matrix = np.array( m ) # A symmetric np matrix storing the tolerance between specie pairs self.custom_values = ( [] ) # A list of tuples storing which species pair tolerances have custom values try: for tup in tuples: self.set_tol(*tup) except: printx( "Error: Could not set custom tolerance value(s).\n" + "All custom entries should be entered using the following form:\n" + "(specie1, specie2, value), where value is the tolerance in Angstroms.", priority=1, ) self.radius_list = [] for i in range(len(self.matrix)): if i == 0: continue x = self.get_tol(i, i) self.radius_list.append(x)