def ChargeMomentOfInertia(picklestruct): SPA = psa.SpacegroupAnalyzer(picklestruct) picklestruct = SPA.get_conventional_standard_structure() Rcm = CenterofMass(picklestruct) #fractional coords I = 0 for site in picklestruct.sites: elemCharge = site.specie.max_oxidation_state coords = site._fcoords dist = coords - Rcm I += elemCharge * np.dot(dist, dist) return I
def MassMomentOfInertia(picklestruct): SPA = psa.SpacegroupAnalyzer(picklestruct) picklestruct = SPA.get_conventional_standard_structure() Rcm = CenterofMass(picklestruct) #fractional coords I = 0 for site in picklestruct.sites: Mass = site.specie.data['Atomic mass'] coords = site._fcoords dist = coords - Rcm I += Mass * np.dot(dist, dist) return I
def CenterofMass(picklestruct): ''' centerofmass in terms of fractional coordinates :param picklestruct: :return: ''' symmetryfinder = psa.SpacegroupAnalyzer(picklestruct) numerator = np.array([0.0, 0.0, 0.0]) denominator = list() for sites in picklestruct.sites: mass = sites.specie.data['Atomic mass'] cellPosition = sites.frac_coords #normalized numerator += cellPosition * mass denominator.append(mass) return numerator / (np.mean(denominator))
def structure_symmetry(): if is_pbc(): struct = readstructure(crystal=True, molecule=False) ast = analyzer.SpacegroupAnalyzer(struct) print("{} : {}".format('Structure Type', 'periodicity')) print("{} : {}".format('Lattice Type', ast.get_lattice_type())) print("{} : {}".format('Space Group ID', ast.get_space_group_number())) print("{} : {}".format('International Symbol', ast.get_space_group_symbol())) print("{} : {}".format('Hall Symbol', ast.get_hall())) return else: struct = readstructure(crystal=False, molecule=True) ast = analyzer.PointGroupAnalyzer(struct) print("{} : {}".format('Structure Type', 'non-periodicity')) print("{} : {}".format('International Symbol', ast.get_pointgroup())) return
def ChargeMomentOfInertia(picklestruct): ''' moment of inertia seems sort of extraneous :param picklestruct: :return: ''' def CenterofMass(picklestruct): #function needs to be imbedded ''' centerofmass in terms of fractional coordinates :param picklestruct: :return: ''' numerator = np.array([0.0, 0.0, 0.0]) denominator = list() for sites in picklestruct.sites: mass = sites.specie.data['Atomic mass'] cellPosition = sites.frac_coords # normalized numerator += cellPosition * mass denominator.append(mass) return numerator / (np.mean(denominator)) SPA = psa.SpacegroupAnalyzer(picklestruct) picklestruct = SPA.get_conventional_standard_structure() Rcm = CenterofMass(picklestruct) #fractional coords I = 0 for site in picklestruct.sites: elemCharge = site.specie.max_oxidation_state coords = site._fcoords dist = coords - Rcm I += elemCharge * np.dot(dist, dist) data = { 'charge moment of inertia': I } return data
def getSpacegroup(kind,inittraj): if kind == 'bulk': pmg = pmgase.AseAtomsAdaptor().get_structure(pickle.loads(inittraj)) return psa.SpacegroupAnalyzer(pmg).get_space_group_number() else: return None
def getCellSymmetryOps(picklestruct): symmetry = psa.SpacegroupAnalyzer(picklestruct) numSGOps = len(symmetry.get_symmetry_operations()) data = {'symmetry ops': numSGOps} return data
def getCellSymmetryOps(picklestruct): symmetry = psa.SpacegroupAnalyzer(picklestruct); numSGOps = len(symmetry.get_symmetry_operations()); return numSGOps;
def getCrystalSystem(picklestruct): symmetry = psa.SpacegroupAnalyzer(picklestruct); numeric = cs.CrystalSysClassFeat(symmetry.get_crystal_system()); return numeric;
def Hall_Number(picklestruct): #return hall_number, which is just another way of listing a spacegroup number symmetryDat = psa.SpacegroupAnalyzer(picklestruct); return symmetryDat.get_symmetry_dataset()['hall_number'];
def rewrite_cif(path: str, outdir: str, remove_disorder: bool = True, remove_duplicates: bool = True, p1: bool = False, clean_symmetry: float = None) -> str: """ Reads cif file and keeps only the relevant parts defined in RELEVANT_KEYS. Sometimes, it is good to loose information ... Args: path (str): Path to input file outdir (str): Path to output directory remove_disorder (bool): If True (default), then disorder groups other than 1 and . are removed. p1 (bool): If True, then we will set the symmetry to P1. clean_symmetry (float): uses spglib to symmetrize the structure with the specified tolerance, set to None if you do not want to use it Returns: outpath (str) """ # ToDo: I want to have it pretty strict with pre-defined keys but maybe a regex can take care of captitiliaztion RELEVANT_KEYS = [ '_cell_volume', '_cell_angle_gamma', '_cell_angle_beta', '_cell_angle_alpha', '_cell_length_a', '_cell_length_b', '_cell_length_c', '_atom_site_label', '_atom_site_fract_x', '_atom_site_fract_y', '_atom_site_fract_z', '_atom_site_charge', '_atom_site_type_symbol', ] RELEVANT_KEYS_NON_P1 = [ '_symmetry_cell_setting', '_space_group_crystal_system', '_space_group_name_hall', '_space_group_crystal_system', '_space_group_it_number', '_symmetry_space_group_name_h-m', '_symmetry_int_tables_number', '_space_group_name_h-m_alt', '_symmetry_tnt_tables_number', '_symmetry_space_group_name_hall', '_symmetry_equiv_pos_as_xyz', '_space_group_symop_operation_xyz', ] LOOP_KEYS = [ '_atom_site_label', '_atom_site_fract_x', '_atom_site_fract_y', '_atom_site_fract_z', '_atom_site_charge', '_atom_site_occupancy' ] NUMERIC_LOOP_KEYS = [ '_atom_site_fract_x', '_atom_site_fract_y', '_atom_site_fract_z', '_atom_site_charge', '_atom_site_occupancy' ] CELL_PROPERTIES = [ '_cell_volume', '_cell_angle_gamma', '_cell_angle_beta', '_cell_angle_alpha', '_cell_length_a', '_cell_length_b', '_cell_length_c', ] try: cf = CifFile.ReadCif(path) image = cf[cf.keys()[0]] if ('_atom_site_fract_x' not in image.keys()) or ( '_atom_site_fract_y' not in image.keys()) or ( '_atom_site_fract_z' not in image.keys()): raise ValueError except ValueError: logger.error( 'the file %s seems to be invalid because we were unable to find ' 'the atomic positions will return input path but ' 'this file will likely cause errors and needs to be checked', path) return path except FileNotFoundError: logger.error('the file %s was not found', path) return path except Exception: logger.error( 'the file %s seems to be invalid will return input path but ' 'this file will likely cause errors and needs to be checked', path) return path else: # First, make sure we have proper atom type labels. if ('_atom_site_type_symbol' not in image.keys()) and ( '_atom_site_symbol' not in image.keys()): # then loop over the label and strip all the floats type_symbols = [] for label in image['_atom_site_label']: type_symbols.append(re.sub('[^a-zA-Z]+', '', label)) image.AddItem('_atom_site_type_symbol', type_symbols) image.AddLoopName('_atom_site_label', '_atom_site_type_symbol') if remove_disorder and '_atom_site_disorder_group' in image.keys(): indices_to_drop = [] logger.info('Removing disorder groups in %s', path) for i, dg in enumerate(image['_atom_site_disorder_group']): if dg not in ('.', '1'): indices_to_drop.append(i) if indices_to_drop: image['_atom_site_type_symbol'] = [ i for j, i in enumerate(image['_atom_site_type_symbol']) if j not in indices_to_drop ] for key in LOOP_KEYS: if key in image.keys(): loop_fixed = [ i for j, i in enumerate(image[key]) if j not in indices_to_drop ] image.RemoveItem(key) image.AddItem(key, loop_fixed) image.AddLoopName('_atom_site_type_symbol', key) if not p1: RELEVANT_KEYS += RELEVANT_KEYS_NON_P1 for key in image.keys(): if key not in RELEVANT_KEYS: image.RemoveItem(key) image['_atom_site_label'] = image['_atom_site_type_symbol'] if p1: image.AddItem('_symmetry_space_group_name_H-M', 'P 1') image.ChangeItemOrder('_symmetry_space_group_name_h-m', -1) image.AddItem('_space_group_name_Hall', 'P 1') image.ChangeItemOrder('_space_group_name_Hall', -1) # remove uncertainty brackets for key in NUMERIC_LOOP_KEYS: if key in image.keys(): image[key] = [ float(re.sub(r'\([^)]*\)', '', s)) for s in image[key] ] for prop in CELL_PROPERTIES: if prop in image.keys(): image[prop] = float(re.sub(r'\([^)]*\)', '', image[prop])) # make filename that is safe name = slugify(Path(path).stem) outpath = os.path.join(outdir, '.'.join([name, 'cif'])) with open(outpath, 'w') as f: f.write(cf.WriteOut() + '\n') if clean_symmetry: crystal = Structure.from_file(outpath) spa = analyzer.SpacegroupAnalyzer(crystal, 0.1) crystal = spa.get_refined_structure() crystal.to(filename=outpath) if remove_duplicates: atoms = read(outpath) get_duplicate_atoms(atoms, 0.5, delete=True) write(outpath, atoms) return outpath
def elastic_analysis(): filename = 'OUTCAR' step_count = 1 check_file(filename) proc_str = "Reading Data From " + filename + " File ..." procs(proc_str, step_count, sp='-->>') outcar = Outcar(filename) outcar.read_elastic_tensor() cij_tensor = np.array(outcar.data['elastic_tensor']) filename = 'vasprun.xml' step_count += 1 check_file(filename) proc_str = "Reading Data From " + filename + " File ..." procs(proc_str, step_count, sp='-->>') vsr = Vasprun(filename) struct0 = vsr.structures[0] natom = struct0.num_sites weight = struct0.composition.weight volume = struct0.lattice.volume ## converting the units volume *= 1.0e-30 ## from Angstrom to meters weight *= weight * 1.0e-3 ## from gram to kg density = weight / (volume * Avogadro) asa = analyzer.SpacegroupAnalyzer(struct0) # lat_type=asa.get_crystal_system() crys_type = asa.get_lattice_type() ## Redefining the Cij matrix into the correct Voigt notation since VASP's OUTCAR has a different order ## In VASP: Columns and rows are listed as: 1, 2, 3, 6, 4, 5 ## In this format OUTCAR's C44 values would be actually C66, C55 would be C44, and C66 would be C55. ## OUTCAR follows the below order: ## [C11 C12 C13 C16 C14 C15] ## [C21 C22 C23 C26 C24 C25] ## [C31 C32 C33 C36 C34 C35] ## [C61 C62 C63 C66 C64 C65] ## [C41 C42 C43 C46 C44 C45] ## [C51 C52 C53 C56 C54 C55] cnew = np.zeros((6, 6)) snew = np.zeros((6, 6)) cnew = np.copy(cij_tensor) for j in range(0, 6): cnew[3][j] = cij_tensor[4][j] cnew[4][j] = cij_tensor[5][j] cnew[5][j] = cij_tensor[3][j] ctemp = np.zeros((6, 6)) ctemp = np.copy(cnew) for i in range(0, 6): cnew[i][3] = cnew[i][4] cnew[i][4] = cnew[i][5] cnew[i][5] = ctemp[i][3] # Change the units of Cij from kBar to GPa cnew = cnew / 10.0 proc_str = "\n Modified elastic tensor in correct order (in GPa units)" print(proc_str) fmt = "%7.3f " * 6 for i in range(6): print(fmt % tuple(cnew[i, :])) def check_symmetric(a, tol=1e-8): return np.allclose(a, a.T, atol=tol) print( '\n Checking if the elastic tensor is symmetric: i.e. Cij = Cji: %5s' % check_symmetric(cnew)) print("\n Eigen Values of the elastic tensor:") evals = LA.eigvals(cnew) print(fmt % tuple(evals)) if np.all(evals) > 0.0: print("\n All eigen values are positive indicating elastic stability.") else: print( "\n ATTENTION: One or more eigen values are negative indicating elastic instability." ) calc_elastic_prop(cnew, snew, crys_type, density, weight, natom)