def primitive(self) -> Structure: if self._primitive is None: primitive = \ spglib.find_primitive(self.cell, symprec=self.symprec, angle_tolerance=self.angle_tolerance) if primitive is None: raise ViseSymmetryError( "Spglib couldn't find the primitive cell. " "Change the symprec and/or angle_tolerance.") else: # To manage spglib cyclic behavior, we need to run this again. second_primitive = cell_to_structure( spglib.find_primitive(primitive, symprec=self.symprec, angle_tolerance=self.angle_tolerance) ).get_sorted_structure() primitive = cell_to_structure(primitive) if primitive != second_primitive: if first_structure_is_primitive(primitive, second_primitive): self._primitive = primitive self._second_primitive = second_primitive else: self._primitive = second_primitive self._second_primitive = primitive else: self._primitive = primitive return self._primitive
def test_find_primitive(self): for fname in self._filenames: spgnum = int(fname.split('-')[1]) cell = read_vasp("./data/%s" % fname) if 'distorted' in fname: dataset = get_symmetry_dataset(cell, symprec=1e-1) primitive = find_primitive(cell, symprec=1e-1) else: dataset = get_symmetry_dataset(cell, symprec=1e-5) primitive = find_primitive(cell, symprec=1e-5) spg_type = get_spacegroup_type(dataset['hall_number']) c = spg_type['international_short'][0] if c in ['A', 'B', 'C', 'I']: multiplicity = 2 elif c == 'F': multiplicity = 4 elif c == 'R': self.assertEqual(spg_type['choice'], 'H') if spg_type['choice'] == 'H': multiplicity = 3 else: # spg_type['choice'] == 'R' multiplicity = 1 else: multiplicity = 1 self.assertEqual(len(dataset['std_types']), len(primitive[2]) * multiplicity, msg=("multi: %d, %s" % (multiplicity, fname)))
def test_find_primitive(self): for fname in self._filenames: cell = read_vasp(fname) if 'distorted' in fname: dataset = get_symmetry_dataset(cell, symprec=1e-1) primitive = find_primitive(cell, symprec=1e-1) else: dataset = get_symmetry_dataset(cell, symprec=1e-5) primitive = find_primitive(cell, symprec=1e-5) spg_type = get_spacegroup_type(dataset['hall_number']) c = spg_type['international_short'][0] if c in ['A', 'B', 'C', 'I']: multiplicity = 2 elif c == 'F': multiplicity = 4 elif c == 'R': self.assertEqual(spg_type['choice'], 'H') if spg_type['choice'] == 'H': multiplicity = 3 else: # spg_type['choice'] == 'R' multiplicity = 1 else: multiplicity = 1 self.assertEqual(len(dataset['std_types']), len(primitive[2]) * multiplicity, msg=("multi: %d, %s" % (multiplicity, fname)))
def get_sym(cell, symprec=1e-5, print_atom=False, print_analysis=True): '''Giving a cell, return symmetry analysis''' # spglib only work with tuple cell = tuple(cell) #Space group info spg_label, spg_number = spglib.get_spacegroup(cell, symprec).split(' ') spg_number = spg_number.split("(")[1].split(")")[0] Schoenflies_label = spglib.get_spacegroup(cell, symprec, symbol_type=1).split(' ')[0] sym = spglib.get_symmetry(cell, symprec) rotations = sym['rotations'] translations = sym['translations'] equi_atoms = sym['equivalent_atoms'] is_std = is_prim = False std_cell = spglib.refine_cell(cell, symprec) prim_cell = spglib.find_primitive(cell, symprec) if compare_cells(cell, std_cell): is_std = True if compare_cells(cell, prim_cell): is_prim = True atoms = utils.convert_atomtype(cell[2]) if print_analysis == True: #Cell info std_cell = spglib.refine_cell(cell, symprec) prim_cell = spglib.find_primitive(cell, symprec) #Print misc.print_msg("Spacegroup number : %s" % (spg_number)) misc.print_msg("Short International symbol : %s" % (spg_label)) misc.print_msg("Schoenflies symbol : %s" % (Schoenflies_label)) if print_atom == True: misc.print_msg("Atoms list (No. - Sym - Symbol):") for i, atom in enumerate(atoms): misc.print_msg("%3d %3d %s" % (i + 1, equi_atoms[i] + 1, atom)) misc.print_msg("Irreducible atoms:") for i, index in enumerate(np.unique(equi_atoms)): coord = cell[1][index] misc.print_msg( "%3d %3s %7f5 %7f5 %7f5" % (i + 1, atoms[index], coord[0], coord[1], coord[2])) misc.print_msg("Number of irreducible atoms : %d" % (np.unique(equi_atoms).shape[0])) misc.print_msg("Standard cell : %r" % (is_std)) misc.print_msg("Primitive cell : %r" % (is_prim)) else: # Return an standard cell object with irreducible atoms irred_idx = np.unique(equi_atoms) lattice = cell[0] irred_coord = cell[1][irred_idx, :] irred_label = np.array(cell[2])[irred_idx] irred_cell = (lattice, irred_coord, irred_label) return irred_cell, int(spg_number), spg_label, rotations, translations
def find_primitive_cell(structure): """ uses spglib find_primitive to find the primitive cell params: AiiDa structure data returns: list of new AiiDa structure data """ # TODO: if refinced structure is the same as given structure # return the given structure (Is this good practise for prov?) from spglib import find_primitive from ase.atoms import Atoms symprec = 1e-7 #print('old {}'.format(len(structure.sites))) ase_structure = structure.get_ase() lattice, scaled_positions, numbers = find_primitive(ase_structure, symprec=symprec) new_structure_ase = Atoms(numbers, scaled_positions=scaled_positions, cell=lattice, pbc=True) new_structure = StructureData(ase=new_structure_ase) #print('new {}'.format(len(new_structure.sites))) new_structure.label = structure.label + ' primitive' new_structure.description = structure.description + ' primitive cell' return new_structure
def cif2structure(filename, primitive=False, symprec=0.001): """ Read a given file as a cif file, convert it into a ase atoms object and finally into a pychemia structure :param filename: (str) Filename of the CIF file that will be read :param primitive: (boolean) if primitive cell should be returned :param symprec: (float) Desired precision for computing the primitive cell :return: """ aseatoms = _ase.io.read(filename) if primitive and USE_SPGLIB: lattice, scaled_positions, numbers = spg.find_primitive( aseatoms, symprec) if lattice is not None and scaled_positions is not None and numbers is not None: fin_atoms = _ase.atoms.Atoms(cell=lattice, scaled_positions=scaled_positions, numbers=numbers, pbc=True) else: fin_atoms = aseatoms else: fin_atoms = aseatoms ret = ase2pychemia(fin_atoms) return ret
def get_primitive_cell(axis, atom_pos): import spglib import numpy as np L = np.mat(axis) pos = [] atom_type = [] atom_dic = {} type_dic = {} index = 0 for line in atom_pos: pos.append(line[0:3]) if not line[4] in atom_dic.keys(): atom_dic[line[4]] = index type_dic[index] = [line[3], line[4]] index = index + 1 atom_type.append(atom_dic[line[4]]) D = np.mat(pos) Cell = (L, D, atom_type) prim_cell = spglib.find_primitive(Cell, symprec=2e-3) equ_atoms = spglib.get_symmetry_dataset(prim_cell, symprec=2e-3)['equivalent_atoms'] prim_axis = prim_cell[0].tolist() prim_pos = prim_cell[1].tolist() prim_type = prim_cell[2].tolist() prim_atom_pos = [] for i in range(len(prim_pos)): prim_atom_pos.append(prim_pos[i] + type_dic[prim_type[i]]) sym = spglib.get_spacegroup(Cell, symprec=2e-3).split('(')[1].split(')')[0] [new_axis, new_pos, sym_typ] = axis_rotation(prim_axis, prim_atom_pos, sym) return [new_axis, new_pos, sym_typ]
def _except_main(types, coords, symprec): types = np.array(types, dtype=int) fracs = np.array(coords.pop('fracs')) lattice = np.array(coords.pop('lattice')) cell = (lattice, fracs, types) # check primitiveness # alas, SPGLIB's symmetry dataset does not give any reliable way to # check this. # # FIXME this should be checked sooner in RSP2, not after expensive relaxation prim = spglib.find_primitive(cell, symprec=symprec) if not prim: SpgError.throw() prim_lattice = prim[0] vol_ratio = abs(np.linalg.det(lattice) / np.linalg.det(prim_lattice)) if abs(abs(vol_ratio) - 1) > 1e-4: return {"Err": "rsp2 requires the input to be a primitive cell"} ds = spglib.get_symmetry_dataset(cell, symprec=symprec) if not ds: SpgError.throw() # you can't JSON-serialize numpy types def un_numpy_ify(d): if isinstance(d, dict): return {k: un_numpy_ify(v) for (k, v) in d.items()} if isinstance(d, list): return [un_numpy_ify(x) for x in d] if hasattr(d, 'tolist'): return d.tolist() if isinstance(d, (float, int, str)): return d raise TypeError return {"Ok": un_numpy_ify(ds)}
def get_primitive_cell(atoms, tol=1e-8): """Atoms object interface with spglib primitive cell finder: https://atztogo.github.io/spglib/python-spglib.html#python-spglib Parameters: ----------- atoms : object Atoms object to search for a primitive unit cell. tol : float Tolerance for floating point rounding errors. Returns: -------- primitive cell : object The primitive unit cell returned by spglib if one is found. """ lattice = atoms.cell positions = atoms.get_scaled_positions() numbers = atoms.get_atomic_numbers() cell = (lattice, positions, numbers) _lattice, _positions, _numbers = spglib.find_primitive(cell, symprec=tol) atoms = Gratoms(symbols=_numbers, cell=_lattice, pbc=atoms.pbc) atoms.set_scaled_positions(_positions) return atoms
def cell_to_prim(cell, symprec=1e-5): prim_cell = spglib.find_primitive(cell, symprec) if compare_cells(cell, prim_cell): print('Unit cell is already a primitive cell. Nothing changes') return cell else: print('Unit cell was transformed to a primitive cell') return prim_cell
def get_sym(cell, symprec=1e-5, print_atom=False, export_operator=False): #Space group info intl_label, number = spglib.get_spacegroup(cell, symprec).split(' ') number = number.split("(")[1].split(")")[0] Schoenflies_label = spglib.get_spacegroup(cell, symprec, symbol_type=1).split(' ')[0] sym = spglib.get_symmetry(cell, symprec) rotations = sym['rotations'] translations = sym['translations'] equi_atoms = sym['equivalent_atoms'] is_std = is_prim = False std_cell = spglib.refine_cell(cell, symprec) prim_cell = spglib.find_primitive(cell, symprec) if compare_cells(cell, std_cell): is_std = True if compare_cells(cell, prim_cell): is_prim = True atoms = utils.convert_atomtype(cell[2]) if export_operator == False: #Cell info std_cell = spglib.refine_cell(cell, symprec) prim_cell = spglib.find_primitive(cell, symprec) #Print misc.print_msg("Spacegroup number : %s" % (number)) misc.print_msg("Short International symbol : %s" % (intl_label)) misc.print_msg("Schoenflies symbol : %s" % (Schoenflies_label)) if print_atom == True: misc.print_msg("Atoms list (No. - Sym - Symbol):") for i, atom in enumerate(atoms): misc.print_msg("%3d %3d %s" % (i + 1, equi_atoms[i] + 1, atom)) misc.print_msg("Irreducible atoms:") for i, index in enumerate(np.unique(equi_atoms)): misc.print_msg("%3d %3s %7f5 %7f5 %7f5" % (i + 1, atoms[index], cell[1][index][0], cell[1][index][1], cell[1][index][2])) misc.print_msg("Number of irreducible atoms : %d" % (np.unique(equi_atoms).shape[0])) misc.print_msg("Standard cell : %r" % (is_std)) misc.print_msg("Primitive cell : %r" % (is_prim)) return is_std, is_prim else: return (number, intl_label), equi_atoms, rotations, translations
def to_primitive(structure): """Returns aiida StructureData with primitive cell""" from aiida.orm import StructureData cell, positions, numbers = spglib.find_primitive(structure.get_ase()) ase_struct = Atoms(numbers, scaled_positions=positions, cell=cell, pbc=True) return StructureData(ase=ase_struct)
def check_if_primitive(atoms: "ase.atoms.Atoms") -> None: """ Checks if input configuration is primitive via spglib. A warning is raised if not. """ cell = (atoms.cell, atoms.get_scaled_positions(), atoms.numbers) lattice, scaled_positions, numbers = find_primitive(cell, symprec=1e-5) is_primitive = (np.abs(lattice - atoms.cell) < 1e-4).all() if not is_primitive: logger.warning("It seems that the structure {} is not primitive.".format(atoms)) logger.warning("This might lead to unexpected results.")
def get_primitive_cell(cell, tolerance=1e-5): numbers = cell.numbers (prim_lattice, prim_positions, prim_numbers) = spglib.find_primitive( (cell.lattice.T, cell.get_points().T, numbers), symprec=tolerance) masses = cell.get_masses() prim_masses = _transfer_masses_by_numbers(prim_numbers, numbers, masses) return Cell(lattice=prim_lattice.T, points=prim_positions.T, numbers=prim_numbers, masses=prim_masses)
def find_primitive_cell(self, symprec = 1e-3, angle_tolerance = 5): """ Find the primitive cell of the structure. Returns: Primitive cell as a Structure object. """ vectors, positions, atomic_numbers = spglib.find_primitive(self.as_tuple(), symprec = symprec, angle_tolerance = angle_tolerance) for i in range(len(positions)): for j in range(3): if fabs(positions[i][j] - 1) < 0.01: positions[i][j] = 0 return Structure(vectors, positions, atomic_numbers)
def cell_to_prim(cell, symprec=1e-5): '''Convert a cell to a primitive cell''' # spglib only work with tuple cell = tuple(cell) prim_cell = spglib.find_primitive(cell, symprec) if compare_cells(cell, prim_cell): print('Unit cell is already a primitive cell. Nothing changes') return cell else: print('Unit cell was transformed to a primitive cell') return prim_cell
def find_primitive(self, symprec: float = 1e-5, angle_tolerance: float = -1) -> Union[Stru, None]: """Return a primitive cell :params symprec: distance tolerance in Cartesian coordinates to find crystal symmetry. Default: 1e-5 :params angle_tolerance: tolerance of angle between basis vectors in degrees to be tolerated in the symmetry finding. Default: -1 :return lattice, atomic scaled positions, and atomic numbersthat are symmetrized following space group type """ import spglib newcell = spglib.find_primitive(self.spgcell, symprec, angle_tolerance) return self.modified_stru(newcell)
def is_primitive(self, symprec=1e-5): """ is_primitive_cell decide if a cell is primitive parameters: cell: Cell object prec: float, the precision to judge, default=1e-5 return: bool """ natoms = len(self.atoms) spg_cell = (self.lattice, self.positions, self.atoms) pnatoms = len(spglib.find_primitive(spg_cell, symprec)[2]) return natoms == pnatoms
def find_primitive(cell, symprec=1e-5): """ A primitive cell is searched in the input cell. When a primitive cell is found, an object of Atoms class of the primitive cell is returned. When not, None is returned. """ lattice, positions, numbers = spglib.find_primitive(cell.totuple(), symprec) if lattice is None: return None else: return Atoms(numbers=numbers, scaled_positions=positions, cell=lattice, pbc=True)
def find_primitive_structure(struct): """Find the structure contained within the reduced cell | Args: | struct (ase.Atoms): structure to find the reduced cell for. | | Returns: | reduced_struct(ase.Atoms): the structure in the reduced cell. """ params = (struct.cell, struct.get_scaled_positions(), struct.numbers) lattice, scaled_positions, numbers = find_primitive(params) reduced_struct = Atoms(cell=lattice, scaled_positions=scaled_positions, numbers=numbers) return reduced_struct
def find_primitive(self): """ Find a primitive version of the unit cell. Returns: A primitive cell in the input cell is searched and returned as an Structure object. If no primitive cell is found, None is returned. """ lattice, scaled_positions, numbers = spglib.find_primitive( self._cell, symprec=self._symprec) species = [self._unique_species[i - 1] for i in numbers] return Structure(lattice, species, scaled_positions, to_unit_cell=True).get_reduced_structure()
def get_primitive_cell(atoms): """ Get the primitive cell for spglib given an atoms object. Parameters: atoms (ase.Atoms): atoms object. Returns: primitive (cell): spglib tuple for primitive cell. """ cell = make_spglib_cell_from_atoms(atoms) primitive = spglib.find_primitive(cell, symprec=1e-10) if primitive is None: return cell return primitive
def get_primitive_unit_cell(directories: Directories, visualise=False): """ Wrapper for reading CIF file, converting it to a primitive unit cell and returning it in data structure, along with lattice vectors :param directories: Structure, input and output strings :param visualise: Output structure as .xyz :return: unit_cell, lattice_vectors: Unit cell and lattice vectors """ structure_name = directories.structure input_dir = directories.input output_dir = directories.output ase_input_data = ase.io.read(input_dir + "/" + structure_name + ".cif", store_tags=False) spg_input = ase_to_spglib(ase_input_data) print("Number of atoms in input", len(spg_input[2])) # Reduce to primitive with SPG #print("Find primitive of conventional structure") lattice, positions, numbers = spglib.find_primitive(spg_input, symprec=1e-5) if visualise: spg_molecule = (lattice, positions, numbers) #spg_show_cell(lattice, positions, numbers) spg_write(output_dir + '/' + structure_name + '_primitive_cell.xyz', spg_molecule, pbc=(1, 1, 1)) # Need lattice vectors column-wise, in np array lattice_vectors = np.zeros(shape=(3, 3)) for i in range(0, 3): lattice_vectors[:, i] = lattice[i] # Need positions in angstrom, not fractional positions_ang = [] for position in positions: positions_ang.append(np.matmul(lattice, position)) # Need unit cell in my molecule format species = [an_to_symbol[an] for an in numbers] unit_cell = atoms.Atoms(species=species, positions=positions_ang) return unit_cell, lattice_vectors
def get_symmetry(unit_cell, base, atoms, verbose=True): """ Symmetry analysis with spglib spglib must be installed """ if _spglib_present: if verbose: print('#####################') print('# Symmetry Analysis #') print('#####################') atomic_number = [] for i in range(len(atoms)): a = atoms[i] b = base[i] atomic_number.append(electronFF[a]['Z']) if verbose: print( f'{i + 1}: {atomic_number[i]} = {2} : [{base[i][0]:.2f}, {base[i][1]:.2f}, {base[i][2]:.2f}]' ) lattice = (unit_cell, base, atomic_number) spgroup = spglib.get_spacegroup(lattice) sym = spglib.get_symmetry(lattice) if verbose: print(" Spacegroup is %s." % spgroup) print(' Crystal has {0} symmetry operation'.format( sym['rotations'].shape[0])) p_lattice, p_positions, p_numbers = spglib.find_primitive(lattice, symprec=1e-5) print("\n########################\n #Basis vectors of primitive Cell:") for i in range(3): print('[{0:.4f}, {1:.4f}, {2:.4f}]'.format(p_lattice[i][0], p_lattice[i][1], p_lattice[i][2])) print('There {0} atoms and {1} species in primitive unit cell:'.format( len(p_positions), p_numbers)) else: print('spglib is not installed') return True
def find_primitive(structure, symprec, angle_tolerance): """compute the primitive cell for an AiiDA structure When computing symmetry, atomic sites with the same **Kind** are treated as symmetrically equivalent (rather than just the atomic elements). Parameters ---------- structure: aiida.StructureData symprec: float Symmetry search tolerance in the unit of length. angle_tolerance: float or None Symmetry search tolerance in the unit of angle degrees. If the value is negative, an internally optimized routine is used to judge symmetry. Returns ------- aiida.StructureData """ from aiida.orm.nodes.data.structure import Site structure = convert_structure(structure, "aiida") cell, int2kind_map = prepare_for_spglib(structure) new_cell = spglib.find_primitive( cell, symprec=symprec, angle_tolerance=-1 if angle_tolerance is None else angle_tolerance, ) if new_cell is None: raise ValueError("standardization of cell failed") new_structure = structure.clone() new_structure.clear_sites() new_structure.cell = new_cell[0].tolist() positions = frac_to_cartesian(new_structure.cell, new_cell[1]) for position, eid in zip(positions, new_cell[2].tolist()): new_structure.append_site( Site(kind_name=int2kind_map[eid], position=position)) return new_structure
def primitive(self, symprec=1e-5): """ primitive structure Arugments: symprec (symmetry tolerance): distance tolerance in Cartesian coordinates to find crystal symmetry """ cell = self.structure.formatting('cell') cell = (cell['lattice'], cell['positions'], cell['numbers']) newcell = spglib.find_primitive(cell, symprec=symprec) if newcell == None: raise StructureFactoryError('the search is filed') else: poscar = self.__toPOSCAR(newcell) self.structure.pop() # delete old object self.structure = Structure().append(poscar) return self.structure
def __init__(self, lattice_vectors, atom_positions, atom_types, hall=None): if not issubclass(atom_types.dtype.type, np.integer): _, atom_types = np.unique(atom_types, return_inverse=True) self.input = BrCell(lattice_vectors, atom_positions, atom_types) self.primitive = BrCell(*spglib.find_primitive(self.input.cell())) self.input_is_primitive = self.input.isapprox(self.primitive) if hall is None: # Relying on spglib to determine the symmetry of this crystal has # the disadvantage that it might introduce an axis rotation, so # that even if the input and primitive cells are equivalent the # *wrong* Hall symbol will be passed to brille. If the input is not # the primitive cell there might *still* be a rotation of axes, but # this needs to be investigated. self.spglib_dict = spglib.get_symmetry_dataset(self.input.cell()) self.hall = self.spglib_dict['hall'] self.spglib_used = True else: self.hall = hall # no error checking self.spglib_used = False
def primitive_atoms(self): """Get primitive atoms.""" phonopy_atoms = ( self._atoms.lattice_mat, self._atoms.frac_coords, self._atoms.atomic_numbers, ) lattice, scaled_positions, numbers = spglib.find_primitive( phonopy_atoms, symprec=self._symprec) elements = self._atoms.elements el_dict = {} for i in elements: el_dict.setdefault(Specie(i).Z, i) prim_elements = [el_dict[i] for i in numbers] prim_atoms = Atoms( lattice_mat=lattice, elements=prim_elements, coords=scaled_positions, cartesian=False, ) return prim_atoms
def find_primitive(cell, symprec=1e-5): """ A primitive cell is searched in the input cell. When a primitive cell is found, an object of Atoms class of the primitive cell is returned. When not, None is returned. From phonopy/structure/symmetry.py """ # return as is when spglib is not installed if not has_spglib: return cell lattice, positions, numbers = spglib.find_primitive(cell, symprec) if lattice is None: return cell else: return Atoms(numbers=numbers, scaled_positions=positions, cell=lattice, pbc=True)
def cif2structure(filename, primitive=False, symprec=0.001): """ Read a given file as a cif file, convert it into a ase atoms object and finally into a pychemia structure :param filename: (str) Filename of the CIF file that will be read :param primitive: (boolean) if primitive cell should be returned :param symprec: (float) Desired precision for computing the primitive cell :return: """ aseatoms = _ase.io.read(filename) if primitive and USE_SPGLIB: lattice, scaled_positions, numbers = spg.find_primitive(aseatoms, symprec) if lattice is not None and scaled_positions is not None and numbers is not None: fin_atoms = _ase.atoms.Atoms(cell=lattice, scaled_positions=scaled_positions, numbers=numbers, pbc=True) else: fin_atoms = aseatoms else: fin_atoms = aseatoms ret = ase2pychemia(fin_atoms) return ret
def primitive(self, symprec=1e-2): """ Returns a Crystal object in the primitive unit cell. Parameters ---------- symprec : float, optional Symmetry-search distance tolerance in Cartesian coordinates [Angstroms]. Returns ------- primitive : Crystal Crystal with primitive cell. Even if the crystal already has a primitive cell, a new crystal is returned. Raises ------ RuntimeError : If primitive cell could not be found. Notes ----- Optional atomic properties (e.g magnetic moment) might be lost in the reduction. """ search = find_primitive(self._spglib_cell(), symprec=symprec) if search is None: raise RuntimeError("Primitive cell could not be found.") lattice_vectors, scaled_positions, numbers = search atoms = [ Atom(int(Z), coords=coords) for Z, coords in zip(numbers, scaled_positions) ] # Preserve whatever subclass this object already is # This is important because some properties can be extracted from # source files (e.g. PWSCF output files) return type(self)(unitcell=atoms, lattice_vectors=lattice_vectors, source=self.source)
def find_primitive(structure, symprec=1e-3, verbosity=0): """ Finds the primitive unit cell of the crystal structure. Args: structure: :class:`simulation.Structure` object with a crystal structure. symprec: Float with the Cartesian distance tolerance. verbosity: Integer with the level of standard output verbosity. Returns: :class:`simulation.Structure` object with the primitive unit cell if successful, the input structure as is, otherwise. """ _check_spglib_install() rev_lookup = dict( zip(structure.site_comp_indices, structure.site_compositions)) cell = spglib.find_primitive(_structure_to_cell(structure), symprec=symprec) if not _check_spglib_success( cell, func='find_primitive', verbosity=verbosity): return structure _cell_to_structure(cell, structure, rev_lookup) return structure
def get_primitive_cell(self): """ get the primitve cell as a new mol object """ assert self.spgcell != None new_spgcell = spglib.find_primitive(self.spgcell) if new_spgcell == None: logger.error("Search for primitive cell failed with symprec %f" % self.symprec) return print(new_spgcell[0]) print(new_spgcell[2]) new_mol = molsys.mol() new_mol.set_natoms(len(new_spgcell[2])) new_mol.set_cell(new_spgcell[0]) new_mol.set_xyz(new_mol.get_real_from_frac(new_spgcell[1])) new_mol.set_elems_number(new_spgcell[2]) # now add the connectivity new_mol.detect_conn() # RS: we could do atomtyping ... but this would have to be a method of mol ... new_mol.set_atypes(["0"] * new_mol.get_natoms()) new_mol.set_nofrags() return new_mol
print(" rotation:") for x in rot: print(" [%2d %2d %2d]" % (x[0], x[1], x[2])) print(" translation:") print(" (%8.5f %8.5f %8.5f)" % (trans[0], trans[1], trans[2])) print('') print("[refine_cell]") print(" Refine distorted rutile structure") lattice, positions, numbers = spglib.refine_cell(rutile_dist, symprec=1e-1) show_cell(lattice, positions, numbers) print('') print("[find_primitive]") print(" Fine primitive distorted silicon structure") lattice, positions, numbers = spglib.find_primitive(silicon_dist, symprec=1e-1) show_cell(lattice, positions, numbers) print('') print("[standardize_cell]") print(" Standardize distorted rutile structure:") print(" (to_primitive=0 and no_idealize=0)") lattice, positions, numbers = spglib.standardize_cell(rutile_dist, to_primitive=0, no_idealize=0, symprec=1e-1) show_cell(lattice, positions, numbers) print('') print("[standardize_cell]") print(" Standardize distorted rutile structure:")
def analyze_QHA_run(Calc, Tmin=1, Tmax=1500, Tsteps=100): ''' Run the QHA analysis procedure on the set of volume calculation Calc. This procedure produces an array with thermodynamic functions for all calculated volumes and given temperature range (the zero temperature is always included). Input ===== Calc - List of calculation data Tmin - Minimum temperature of the scan (K) Tmax - Maximum temperature of the scan (K) Tsteps - Number of temperature steps bdir - base directory (subdirectories host and calculation are located below) Output ====== qha - array of N x Tsteps x #params containing resulting data: V, T, Etot, Fph, P*V, Cv, S, ... for each calculation and each temperature step. ''' phdos={} qha=[] for n,c in enumerate(Calc): PCMUL=len(c.get_atomic_numbers())/len(spglib.find_primitive(c)[2]) # Read the calculated dos clc=c.calc.results phdos[n]=clc['phdos'] #Etot=c.get_potential_energy() Etot=clc['etotal']*Rydberg # Pressure returned in kbar we need to change it to eV/A^3 to get p*V in eV # eV/A^3=160.2176487GPa # kbar=0.1 GPa # eV/A^3 = 1602.176487 kbar P=clc['pressure']/1602.176487 #P=c.get_isotropic_pressure(c.get_stress()) # Scan the temperatures at this volume # Unit cell (primitive) volume in Bohr^3 => (A^3) #V=(calcQHA[idx][2]['A'])**3 #V=clc['volume']*(Bohr**3) V=c.get_volume()/PCMUL # get the ndf from the normalization of dos dos=phdos[n] ndf=round(simps(dos[1],dos[0])) # frequencies/energies # QE outputs dos in cm^-1, let's convert to sane units # Let's convert to energy to include the hbar nu=1e-3*cminv2meV*phdos[n][0] dos=phdos[n][1] # We need to cut the nu to positive range no=array([[o,g] for o,g in zip(nu,dos) if o>0 ]) nu=no[:,0] dos=no[:,1] # correct the normalization for units, negative cut and numerical errors dos=dos/simps(dos,nu) # plot in meV #plot(1e-3*nu,dos,label=n); #legend() #show() # plot the integrand at 300K #T=300 #plot(nu,dos*log(2*sinh(0.5*nu/(k_B*T))),label=idx) # Zero-point energy - $\hbar\omega/2$ for each degree of freedom integrated over $\omega$ F0=ndf*simps(0.5*dos*nu,nu) # Put in special case for the T=0. Just ZPV energy. qhav=[[V,0,Etot,F0,0,0,0,0]] qha.append(qhav) print '# %s: V=%.2f A^3, Etot=%.3f eV, P=%6.1f GPa' % (n, V, Etot, P/GPa) for T in linspace(Tmin,Tmax,Tsteps): # Maradudin book formula - the only one important for thermal expansion # The result is in eV, temperature in K e=0.5*nu/(k_B*T) Fph=ndf*T*k_B*simps(dos*log(2*sinh(e)),nu) Cv=ndf*k_B*simps(e*e*dos/sinh(e)**2,nu) S=k_B*(simps(2*dos*e/(exp(2*e)-1),nu)-simps(dos*(1-exp(-2*e)),nu)) # Alternative formula from QE Sqe=k_B*simps(dos*(e/tanh(e)-log(2*sinh(e))),nu) # Formulas lifted from the QHA code in QE - not correct with current units #q=0.5*a3*nu/T #E_int=simps(a1*dos*nu/tanh(q),nu) #S0=simps(dos*(q/tanh(q)-log(2*sinh(q))),nu) qhav.append([V, T, Etot, Fph, P*V, Cv, S, Sqe]) #print " %5.2f %12f %12f %12f" % (T, Cv, S, Sqe) return array(qha)
def find_primitive(self, symprec=1e-5): new_spglib_cell = spg.find_primitive(self.spglib_cell, symprec=symprec) return self.get_new_structure(new_spglib_cell)
def write_cell_params(fh, a, p): ''' Write the specification of the cell using a traditional A,B,C, alpha, beta, gamma scheme. The symmetry and parameters are determined from the atoms (a) object. The atoms object is translated into a primitive unit cell but *not* converted. This is just an internal procedure. If you wish to work with primitive unit cells in ASE, you need to make a conversion yourself. The cell params are in angstrom. Input ----- fh file handle of the opened pw.in file a atoms object p parameters dictionary Output ------ Primitive cell tuple of arrays: (lattice, atoms, atomic numbers) ''' assert(p['use_symmetry']) # Get a spacegroup name and number for the a sg,sgn=spglib.get_spacegroup(a).split() # Extract the number sgn=int(sgn[1:-1]) # Extract the lattice type ltyp=sg[0] # Find a primitive unit cell for the system # puc is a tuple of (lattice, atoms, atomic numbers) puc=spglib.find_primitive(a) cell=puc[0] apos=puc[1] anum=puc[2] icell=a.get_cell() A=norm(icell[0]) B=norm(icell[1]) C=norm(icell[2]) # Select appropriate ibrav if sgn >= 195 : # Cubic lattice if ltyp=='P': p['ibrav']=1 # Primitive qepc=array([[1,0,0],[0,1,0],[0,0,1]]) elif ltyp=='F': p['ibrav']=2 # FCC qepc=array([[-1,0,1],[0,1,1],[-1,1,0]])/2.0 elif ltyp=='I': p['ibrav']=3 # BCC qepc=array([[1,1,1],[-1,1,1],[-1,-1,1]])/2.0 else : print 'Impossible lattice symmetry! Contact the author!' raise NotImplementedError #a=sqrt(2)*norm(cell[0]) qepc=A*qepc fh.write(' A = %f,\n' % (A,)) elif sgn >= 143 : # Hexagonal and trigonal if ltyp=='P' : p['ibrav']=4 # Primitive qepc=array([[1,0,0],[-1/2,sqrt(3)/2,0],[0,0,C/A]]) elif ltyp=='R' : p['ibrav']=5 # Trigonal rhombohedral raise NotImplementedError else : print 'Impossible lattice symmetry! Contact the author!' raise NotImplementedError qepc=A*qepc fh.write(' A = %f,\n' % (A,)) fh.write(' C = %f,\n' % (C,)) elif sgn >= 75 : raise NotImplementedError elif sgn ==1 : # P1 symmetry - no special primitive cell signal to the caller p['ibrav']=0 return None else : raise NotImplementedError cp=Atoms(cell=puc[0], scaled_positions=puc[1], numbers=puc[2], pbc=True) qepc=Atoms(cell=qepc, positions=cp.get_positions(), numbers=cp.get_atomic_numbers(), pbc=True) return qepc.get_cell(), qepc.get_scaled_positions(), qepc.get_atomic_numbers()