def __init__(self, user_input, make_supercell=False, supercell_matrix=None, isdope=False, dopant=None): """ Args: input: the POSCAR file of initial structure, or the material_id """ self.make_supercell = make_supercell self.supercell_matrix = supercell_matrix self.isdope = isdope self.dopant = dopant # if user_input is a path where store the POSCAR file if (os.path.isfile(user_input)): pos = Poscar.from_file(user_input) struc = SpacegroupAnalyzer(pos.structure) self.init_struc = struc.get_conventional_standard_structure() # user_input is a mp_id else: prim_struc = mpr.get_structure_by_material_id(user_input) struc = SpacegroupAnalyzer(prim_struc) self.init_struc = struc.get_conventional_standard_structure() self.formula = str(self.init_struc.composition.reduced_formula) logging.info("self.formula: {}".format(self.formula))
def setUpClass(cls): si_struct = cls.get_structure("Si") sio2_struct = cls.get_structure("SiO2") sga = SpacegroupAnalyzer(si_struct) si_conventional = sga.get_conventional_standard_structure() sga = SpacegroupAnalyzer(sio2_struct) sio2_conventional = sga.get_conventional_standard_structure() cls.ib = InterfaceBuilder(si_conventional, sio2_conventional) cls.ib.generate_interfaces(substrate_millers=[[1, 0, 0]], film_layers=3, substrate_layers=3)
def conventional_structure(structure_file, fmt="json"): """ Args: structure_file: fmt: Returns: """ cathode = Cathode.from_file(structure_file) spg = SpacegroupAnalyzer(cathode) conv_structure_file = structure_file.split(".")[0] + "_conv" + "." + fmt spg.get_conventional_standard_structure().to(fmt, conv_structure_file)
def setUpClass(cls): si_struct = cls.get_structure("Si") sio2_struct = cls.get_structure("SiO2") sga = SpacegroupAnalyzer(si_struct) si_conventional = sga.get_conventional_standard_structure() sga = SpacegroupAnalyzer(sio2_struct) sio2_conventional = sga.get_conventional_standard_structure() cls.ibs = [] Si_SiO2 = [si_conventional, sio2_conventional] random.shuffle(Si_SiO2) cls.ibs.append(cls.make_ib(*Si_SiO2, [1, 0, 0])) Si_SiO2 = [si_struct, sio2_struct] random.shuffle(Si_SiO2) cls.ibs.append(cls.make_ib(*Si_SiO2, [1, 0, 0])) cls.ibs.append(cls.make_ib(*Si_SiO2, [1, 1, 1]))
def cif2geom_sym2(cif): parser=CifParser.from_string(cif) struct=parser.get_structures()[0] sg = SpacegroupAnalyzer(struct) struct = sg.get_conventional_standard_structure() sg = SpacegroupAnalyzer(struct) geomlines=["CRYSTAL"] geomlines += ["0 0 1"] geomlines += [str(sg.get_spacegroup_number())] cry_sys = sg.get_crystal_system() lattice = struct.lattice if cry_sys == 'trigonal' or cry_sys == 'hexagonal' or cry_sys == 'tetragonal': geomlines += ["%s %s" %(lattice.a,lattice.c)] elif cry_sys == 'cubic': geomlines += ["%s" %(lattice.a)] elif cry_sys == 'triclinic': geomlines += ["%s %s %s %s %s %s" %(lattice.a,lattice.b,lattice.c,lattice.alpha,lattice.beta,lattice.gamma)] elif cry_sys == 'monoclinic': geomlines += ["%s %s %s %s" %(lattice.a,lattice.b,lattice.c,lattice.beta)] elif cry_sys == 'orthorhombic': geomlines += ["%s %s %s" %(lattice.a,lattice.b,lattice.c)] else: print('Error printing symmetrized structure.') quit() ds = sg.get_symmetry_dataset() eq_sites = np.unique(ds['equivalent_atoms']) geomlines += [str(len(eq_sites))] for eq_site in eq_sites: site = struct.sites[eq_site] geomlines += ["%s %s %s %s" %(site.specie.Z+200,site.a,site.b,site.c)] return geomlines,struct
def update_graph(graph_generation_options, unit_cell_choice, struct_or_mol): if not struct_or_mol: raise PreventUpdate struct_or_mol = self.from_data(struct_or_mol) graph_generation_options = self.from_data(graph_generation_options) if isinstance(struct_or_mol, Structure): if unit_cell_choice != "input": if unit_cell_choice == "primitive": struct_or_mol = struct_or_mol.get_primitive_structure() elif unit_cell_choice == "conventional": sga = SpacegroupAnalyzer(struct_or_mol) struct_or_mol = sga.get_conventional_standard_structure() elif unit_cell_choice == "reduced": struct_or_mol = struct_or_mol.get_reduced_structure() graph = self._preprocess_input_to_graph( struct_or_mol, bonding_strategy=graph_generation_options["bonding_strategy"], bonding_strategy_kwargs=graph_generation_options[ "bonding_strategy_kwargs" ], ) self.logger.debug("Constructed graph") return self.to_data(graph)
def make_crystal_img(cif_str, resolution=0.5): struct = Structure.from_str(input_string=cif_str,fmt='cif') sg = SpacegroupAnalyzer(struct) conv_struct = sg.get_conventional_standard_structure() sites = conv_struct.sites nfeats = [3] lattice_lengths = np.asarray(conv_struct.lattice.abc) dimensions = list(np.ceil(lattice_lengths / resolution).astype(int)) img = np.zeros(dimensions+nfeats, dtype=np.float32) for i in range(img.shape[0]): for j in range(img.shape[1]): for k in range(img.shape[2]): x = (float(i)+0.5)/img.shape[0]*lattice_lengths[0] y = (float(j)+0.5)/img.shape[1]*lattice_lengths[1] z = (float(k)+0.5)/img.shape[2]*lattice_lengths[2] pt = np.array([x,y,z]) neighbors = conv_struct.get_sites_in_sphere(pt=pt,r=10) nearest_neighbor = [n[0] for n in sorted(neighbors, key=lambda s: s[1])][0] features = element_features(nearest_neighbor.specie) if all([f != 'failed' for f in features]): img[i,j,k,:] = features else: return 'failed' return img
def parallel_ND(root_dir, save_dir, fnames, max_Miller, sym_thresh): process_name = multiprocessing.current_process().name # initialize ND simulator nd_simulator = ND.NDSimulator(max_Miller=max_Miller) for filename in tqdm(fnames): cifpath = os.path.join(root_dir, "MP_cifs", filename+".cif") assert os.path.isfile(cifpath) cif_struct = Structure.from_file(cifpath) sga = SpacegroupAnalyzer(cif_struct, symprec=sym_thresh) # primitive cell primitive_struct = sga.get_primitive_standard_structure() # skip if this compound contains Ac or Pu (no neutron scattering length available) if (Element('Ac') in primitive_struct.species) or (Element('Pu') in primitive_struct.species): print('containing Ac or Pu, skipped {}'.format(filename), flush=True) continue # compute ND patterns primitive_features = nd_simulator.get_pattern(structure=primitive_struct) # save primitive diffraction pattern np.save(os.path.join(save_dir, filename+"_ND_primitive.npy"), primitive_features) # conventional cell conventional_struct = sga.get_conventional_standard_structure() conventional_features = nd_simulator.get_pattern(structure=conventional_struct) # save primitive diffraction pattern np.save(os.path.join(save_dir, filename+"_ND_conventional.npy"), conventional_features) print('Process {} is done..'.format(process_name), flush=True)
def update_graph(graph_generation_options, unit_cell_choice, repeats, struct_or_mol): struct_or_mol = self.from_data(struct_or_mol) graph_generation_options = self.from_data(graph_generation_options) repeats = int(repeats) if isinstance(struct_or_mol, Structure): if unit_cell_choice != "input": if unit_cell_choice == "primitive": struct_or_mol = struct_or_mol.get_primitive_structure() elif unit_cell_choice == "conventional": sga = SpacegroupAnalyzer(struct_or_mol) struct_or_mol = sga.get_conventional_standard_structure( ) if repeats != 1: struct_or_mol = struct_or_mol * (repeats, repeats, repeats) graph = self._preprocess_input_to_graph( struct_or_mol, bonding_strategy=graph_generation_options["bonding_strategy"], bonding_strategy_kwargs=graph_generation_options[ "bonding_strategy_kwargs"], ) return self.to_data(graph)
def parallel_XRD(root_dir, fnames, wavelength, sym_thresh): # initialize XRD simulator xrd_simulator = XRD.XRDSimulator(wavelength=wavelength) for idx, filename in enumerate(fnames): if (idx+1)%100 == 0: print('this worker finished processing {} materials..'.format(idx+1), flush=True) filepath = os.path.join(root_dir, filename+".cif") try: assert(os.path.isfile(filepath)) except: print('{} file is missing, abort..'.format(filepath)) cif_struct = Structure.from_file(filepath) sga = SpacegroupAnalyzer(cif_struct, symprec=sym_thresh) # conventional cell conventional_struct = sga.get_conventional_standard_structure() _, conventional_recip_latt, conventional_features = \ xrd_simulator.get_pattern(structure=conventional_struct, two_theta_range=None) # save conventional reciprocal lattice vector np.save(os.path.join(root_dir, filename+"_conventional_basis.npy"), \ conventional_recip_latt) # save conventional diffraction pattern np.save(os.path.join(root_dir, filename+"_XRD_conventional.npy"), \ np.array(conventional_features)) print('this process is done..', flush=True)
def parallel_ND(root_dir, fnames, sym_thresh): print('size of this batch:', len(fnames), flush=True) # initialize XRD simulator nd_simulator = ND.NDSimulator() for idx, filename in enumerate(fnames): if (idx+1)%100 == 0: print('this worker finished processing {} materials..'.format(idx+1), flush=True) filepath = os.path.join(root_dir, filename+".cif") try: assert(os.path.isfile(filepath)) except: print('{} file is missing, abort..'.format(filepath)) cif_struct = Structure.from_file(filepath) sga = SpacegroupAnalyzer(cif_struct, symprec=sym_thresh) # conventional cell conventional_struct = sga.get_conventional_standard_structure() # skip if this compound contains Ac or Pu (no neutron scattering length available) if (Element('Ac') in conventional_struct.species) or (Element('Pu') in conventional_struct.species): print('containing Ac or Pu, skipped {}'.format(filename), flush=True) continue _, conventional_recip_latt, conventional_nd_features = \ nd_simulator.get_pattern(structure=conventional_struct, two_theta_range=None) # addtional safe guard in parallel processing try: assert(np.array_equal(conventional_recip_latt, np.load(os.path.join(root_dir, filename+"_conventional_basis.npy")))) except: print('file {} reciprocal lattice mismatch'.format(filename), flush=True) # save conventional diffraction pattern np.save(os.path.join(root_dir, filename+"_ND_conventional.npy"), \ np.array(conventional_nd_features)) print('this process is done..', flush=True)
def main_func(mpid='',mat=None,parameters={}): if mpid !='': json_dat=parameters['json_dat'] data = loadfn(json_dat, cls=MontyDecoder) for d in data: mmpid= str(d['mpid']) if mmpid==mpid: fin= (d['structure']) break strt = fin#mp.get_structure_by_material_id(mpid) sg_mat = SpacegroupAnalyzer(strt) mat_cvn = sg_mat.get_conventional_standard_structure() mat_cvn.sort() if int(strt.composition._natoms)==int(mat_cvn.composition._natoms): mat= Poscar(mat_cvn) else: mat=Poscar(strt) mpid=mpid.replace('-','_') mpid=str('bulk@')+str(mpid) mat.comment=mpid main(p=mat,parameters=parameters)
def parallel_computing(root_dir, fnames, wavelength, sym_thresh): # initialize XRD simulator xrd_simulator = xrd.XRDSimulator(wavelength=wavelength) for idx, filename in enumerate(fnames): if (idx + 1) % 100 == 0: print('this worker finished processing {} materials..'.format(idx + 1), flush=True) with open(os.path.join(root_dir, filename + ".cif")) as f: cif_struct = Structure.from_str(f.read(), fmt="cif") sga = SpacegroupAnalyzer(cif_struct, symprec=sym_thresh) # conventional cell conventional_struct = sga.get_conventional_standard_structure() _, conventional_recip_latt, conventional_features = \ xrd_simulator.get_pattern(structure=conventional_struct) # save conventional reciprocal lattice vector np.save(os.path.join(root_dir, filename+"_conventional_basis.npy"), \ conventional_recip_latt) # save conventional diffraction pattern np.save(os.path.join(root_dir, filename+"_conventional.npy"), \ np.array(conventional_features)) # primitive cell primitive_struct = sga.get_primitive_standard_structure() _, primitive_recip_latt, primitive_features = \ xrd_simulator.get_pattern(structure=primitive_struct) # save primitive reciprocal lattice vector np.save(os.path.join(root_dir, filename+"_primitive_basis.npy"), \ primitive_recip_latt) # save primitive diffraction pattern np.save(os.path.join(root_dir, filename+"_primitive.npy"), \ np.array(primitive_features))
def _create_surface(self): ''' This method will create the surface structure to relax Returns: surface_atoms_constrained `ase.Atoms` object of the surface to submit to Fireworks for relaxation ''' # Get the bulk and convert to `pymatgen.Structure` object with open(self.input().path, 'rb') as file_handle: bulk_doc = pickle.load(file_handle) bulk_atoms = make_atoms_from_doc(bulk_doc) bulk_structure = AseAtomsAdaptor.get_structure(bulk_atoms) # Use pymatgen to turn the bulk into a surface sga = SpacegroupAnalyzer(bulk_structure, symprec=0.1) bulk_structure = sga.get_conventional_standard_structure() gen = SlabGenerator(initial_structure=bulk_structure, miller_index=self.miller_indices, min_slab_size=self.min_height, **self.slab_generator_settings) surface_structure = gen.get_slab(self.shift, tol=self.get_slab_settings['tol']) # Convert the surface back to an `ase.Atoms` object and constrain # subsurface atoms surface_atoms = AseAtomsAdaptor.get_atoms(surface_structure) surface_atoms_constrained = self.__constrain_surface(surface_atoms) return surface_atoms_constrained
def main(p=None,vac_cal=True,surf_cal=True,phon_cal=True, parameters={}): #p=Poscar.from_file("POSCAR") c_size=parameters['c_size'] vac_size=parameters['vac_size'] surf_size=parameters['surf_size'] phon_size=parameters['phon_size'] sg_mat = SpacegroupAnalyzer(p.structure) mat_cvn = sg_mat.get_conventional_standard_structure() dim1=int((float(c_size)/float( max(abs(mat_cvn.lattice.matrix[0])))))+1 dim2=int(float(c_size)/float( max(abs(mat_cvn.lattice.matrix[1]))))+1 dim3=int(float(c_size)/float( max(abs(mat_cvn.lattice.matrix[2]))))+1 cellmax=max(dim1,dim2,dim3) tmp=mat_cvn.copy() mat_cvn.make_supercell([dim1,dim2,dim3]) #print "dim1 dim2 dim3",dim1,dim2,dim3 #mat_cvn.make_supercell([dim1,dim2,dim3]) mat_pos=Poscar(mat_cvn) mat_pos.comment=str(p.comment) #print ('execcccccc',parameters['exec']) #print mat_pos #toten,final_str,forces=run_job(mat=mat_pos,parameters = parameters) #do_phonons(strt=final_str,parameters=parameters) try: print ('in elastic calc') toten,final_str,forces=run_job(mat=mat_pos,parameters = parameters) print ('done elastic calc') except: pass vac=vac_antisite_def_struct_gen(c_size=vac_size,struct=final_str) def_list,header_list=def_energy(vac=vac,parameters = parameters) #print def_list,header_list try: if vac_cal==True: print ('in defect calc') vac=vac_antisite_def_struct_gen(c_size=vac_size,struct=final_str) def_list,header_list=def_energy(vac=vac,parameters = parameters) print ('done defect calc',def_list,header_list) except: pass try: if surf_cal==True: print ('in surf calc') surf=pmg_surfer(mat=final_str, min_slab_size=surf_size,vacuum=25,max_index=3) surf_list,surf_header_list=surf_energy(surf=surf,parameters = parameters) print ('done surf calc',surf_list,surf_header_list) except: pass try: if phon_cal==True: print ('in phon calc') cwd=str(os.getcwd()) if not os.path.exists("Phonon"): os.mkdir("Phonon") os.chdir("Phonon") do_phonons(strt=final_str,parameters=parameters) print ('done phon calc') os.chdir(cwd) except: pass
def __calculate_unit_slab(self): ''' Calculates the height of the smallest unit slab from a given bulk and Miller cut Saved attributes: unit A `pymatgen.Structure` instance for the unit slab unit_slab_height The height of the unit slab in Angstroms ''' # Luigi will probably call this method multiple times. We only need to # do it once though. if not hasattr(self, 'unit_slab_height'): # Delete some slab generator settings that we don't care about for a # unit slab slab_generator_settings = utils.unfreeze_dict( self.slab_generator_settings) del slab_generator_settings['min_vacuum_size'] del slab_generator_settings['min_slab_size'] # Instantiate a pymatgen `SlabGenerator` bulk_structure = AseAtomsAdaptor.get_structure(self.bulk_atoms) sga = SpacegroupAnalyzer(bulk_structure, symprec=0.1) bulk_structure = sga.get_conventional_standard_structure() gen = SlabGenerator(initial_structure=bulk_structure, miller_index=self.miller_indices, min_vacuum_size=0., min_slab_size=1., **slab_generator_settings) # Generate the unit slab and find its height self.unit_slab = gen.get_slab(self.shift, tol=self.get_slab_settings['tol']) self.unit_slab_height = gen._proj_height
def __init__(self,stru_source): try: stru = m.get_structure_by_material_id(stru_source) self.mpid = stru_source except: stru = Structure.from_file(stru_source) self.mpid = '' spa = SpacegroupAnalyzer(stru) self.stru = spa.get_primitive_standard_structure() self.stru_prim = spa.get_primitive_standard_structure() self.stru_conv = spa.get_conventional_standard_structure() formula = self.stru.composition.reduced_formula self.name = self.mpid+'_'+formula if self.mpid else formula try: self.bs = m.get_bandstructure_by_material_id(self.mpid) self.is_spin_polarized = self.bs.is_spin_polarized bs_exist = True self.pbevbm = self.bs.get_vbm() self.pbecbm = self.bs.get_cbm() self.kpbevbm = self.pbevbm['kpoint'].frac_coords self.kpbecbm = self.pbecbm['kpoint'].frac_coords except: bs_exist = False self.kpbevbm = None self.kpbecbm = None self.bs = None if not bs_exist: self.is_spin_polarized = False for elem in stru.composition.elements: if elem.is_transition_metal: self.is_spin_polarized = True break
def surfer(mpid='', vacuum=15, layers=2, mat=None, max_index=1, write_file=True): """ ASE surface bulder Args: vacuum: vacuum region mat: Structure object max_index: maximum miller index min_slab_size: minimum slab size Returns: structures: list of surface Structure objects """ if mat == None: with MPRester() as mp: mat = mp.get_structure_by_material_id(mpid) if mpid == '': print('Provide structure') sg_mat = SpacegroupAnalyzer(mat) mat_cvn = sg_mat.get_conventional_standard_structure() mat_cvn.sort() indices = get_symmetrically_distinct_miller_indices(mat_cvn, max_index) ase_atoms = AseAtomsAdaptor().get_atoms(mat_cvn) structures = [] pos = Poscar(mat_cvn) try: pos.comment = str('sbulk') + str('@') + str('vac') + str(vacuum) + str( '@') + str('layers') + str(layers) except: pass structures.append(pos) if write_file == True: mat_cvn.to(fmt='poscar', filename=str('POSCAR-') + str('cvn') + str('.vasp')) for i in indices: ase_slab = surface(ase_atoms, i, layers) ase_slab.center(vacuum=vacuum, axis=2) slab_pymatgen = AseAtomsAdaptor().get_structure(ase_slab) slab_pymatgen.sort() surf_name = '_'.join(map(str, i)) pos = Poscar(slab_pymatgen) try: pos.comment = str("Surf-") + str(surf_name) + str('@') + str( 'vac') + str(vacuum) + str('@') + str('layers') + str(layers) except: pass if write_file == True: pos.write_file(filename=str('POSCAR-') + str("Surf-") + str(surf_name) + str('.vasp')) structures.append(pos) return structures
def pattern_from_mpid_or_struct(rad_source, mp_time, struct_time, mpid, struct): if (struct_time is None) or (mp_time is None): raise PreventUpdate if struct_time > mp_time: if struct is None: raise PreventUpdate sga = SpacegroupAnalyzer(self.from_data(struct)) struct = ( sga.get_conventional_standard_structure() ) # always get conventional structure elif mp_time >= struct_time: if mpid is None: raise PreventUpdate mpid = mpid["mpid"] with MPRester() as mpr: struct = mpr.get_structure_by_material_id( mpid, conventional_unit_cell=True ) xrdc = XRDCalculator(wavelength=rad_source) data = xrdc.get_pattern(struct, two_theta_range=None) return data.as_dict()
def smart_vac(strt=None,tol=0.1): """ Umbrell function for vacancy formation energies with convergence Args: strt: Structure object tol: defect energy convergence tolerance in eV Returns: def_list: list of defect energies def_header_list: list of defect names """ vac_arr=[] sg_mat = SpacegroupAnalyzer(strt) mat_cvn = sg_mat.get_conventional_standard_structure() mat_cvn.sort() cellmax=1 #int(mat_cvn.composition.num_atoms)+int(mat_cvn.ntypesp)#5 ase_atoms = AseAtomsAdaptor().get_atoms(mat_cvn) ase_atoms=ase_atoms*(cellmax,cellmax,cellmax) #if len(ase_atoms) >200: # cellmax=1 #else: # cellmax=2 #cellmax=int(mat_cvn.composition.num_atoms)+int(mat_cvn.ntypesp)#5 print ("type of trt is= celmmax",type(strt),cellmax) try: print ("int(strt.composition.num_atoms)",int(strt.composition.num_atoms)) print (int(strt.ntypesp)) except: pass #cellmax=int(strt.composition.num_atoms)+int(strt.ntypesp) vac_done=0 vac=vac_antisite_def_struct_gen(cellmax=cellmax,struct=strt) def_list=[100000 for y in range(len(vac)-1)] while vac_done !=1: vac=vac_antisite_def_struct_gen(cellmax=cellmax,struct=strt) if vac not in vac_arr: vac_arr.append(vac) print ("in smart_vac(strt=None), cellmax,vac,vac_done=",cellmax,vac,vac_done) def_list2,header_list=def_energy(vac=vac) diff=matrix(def_list)-matrix(def_list2) diff_arr=np.array(diff).flatten() print ("in smart_vac(strt=None diff_arr=",diff_arr) if any(diff_arr)>tol: # for el in diff_arr: # if abs(el)>tol : # print ("in smart_vac(strt=None abs_el=",abs(el)) vac_done=0 cellmax=cellmax+1 ase_atoms = AseAtomsAdaptor().get_atoms(mat_cvn) ase_atoms=ase_atoms*(cellmax,cellmax,cellmax) if len(ase_atoms) >100: vac_done=1 def_list=def_list2 else: vac_done=1 # cellmax=cellmax+1 return def_list,header_list
def smart_surf(strt=None, tol=0.1): """ Umbrell function for surface energies with convergence Args: strt: Structure object tol: surface energy convergence tolerance in eV Returns: surf_list: list of surface energies surf_header_list: list of surface names """ sg_mat = SpacegroupAnalyzer(strt) mat_cvn = sg_mat.get_conventional_standard_structure() mat_cvn.sort() layers = 2 indices = get_symmetrically_distinct_miller_indices(mat_cvn, 1) ase_atoms = AseAtomsAdaptor().get_atoms(mat_cvn) for i in indices: ase_slab = surface(ase_atoms, i, layers) ase_slab.center(vacuum=15, axis=2) if len(ase_slab) < 50: layers = 3 surf_arr = [] surf_done = 0 surf = surfer(mat=strt, layers=layers) surf_list = [100000 for y in range(len(surf) - 1)] print("in smart_surf :surf,surf_list=", surf, surf_list) while surf_done != 1: layers = layers + 1 indices = get_symmetrically_distinct_miller_indices(mat_cvn, 1) ase_atoms = AseAtomsAdaptor().get_atoms(mat_cvn) for i in indices: ase_slab = surface(ase_atoms, i, layers) ase_slab.center(vacuum=15, axis=2) if len(ase_slab) > 100: surf_done = 1 if (ase_slab.get_cell()[2][2]) > 40: surf_done = 1 surf = surfer(mat=strt, layers=layers) if surf not in surf_arr: surf_arr.append(surf) surf_list2, surf_header_list = surf_energy(surf=surf) print("in smart_surf :surf2,surf_list2=", surf_list2, surf_header_list) diff = matrix(surf_list) - matrix(surf_list2) print("in smart_surf :surf3,surf_list3=", matrix(surf_list), matrix(surf_list2)) diff_arr = np.array(diff).flatten() if any(diff_arr) > tol: #for el in diff_arr: # if abs(el)>tol : # print ("in smart_surf :abs el=",abs(el)) surf_done = 0 surf_list = surf_list2 else: surf_done = 1 return surf_list, surf_header_list
def vac_intl(cellmax=2, mpid='', struct=None): """ Vacancy and interstitial generator Args: cellmax: maximum cell size struct: Structure object Returns: def_str: defect structures """ if struct == None: with MPRester() as mp: struct = mp.get_structure_by_material_id(mpid) if mpid == '': print("Provide structure") sg_mat = SpacegroupAnalyzer(struct) struct = sg_mat.get_conventional_standard_structure() def_str = [] count = 0 cell_arr = [cellmax, cellmax, cellmax] vac = Vacancy(struct, {}, {}) for el in list(struct.symbol_set): scs = vac.make_supercells_with_defects(cell_arr, el) for i in range(len(scs)): if i == 0: pos = Poscar(scs[i]) pos.comment = str('bulk') + str('.') + str('cellmax') + str( cellmax) if count == 0: def_str.append(pos) count = count + 1 else: pos = Poscar(scs[i]) pos.comment = str('vac') + str('cellmax') + str(cellmax) + str( '@') + str(vac.get_defectsite_multiplicity(i)) + str( 'Element') + str(el) if pos not in def_str: def_str.append(pos) struct_valrad_eval = ValenceIonicRadiusEvaluator(struct) val = struct_valrad_eval.valences rad = struct_valrad_eval.radii struct_val = val struct_rad = rad intl = Interstitial(struct, val, rad) for el in struct.composition.elements: scs = intl.make_supercells_with_defects(cell_arr, el) for i in range(1, len(scs)): pos = Poscar(scs[i]) pos.comment = str('intl') + str('cellmax') + str(cellmax) + str( '@') + str(intl.get_defectsite_coordination_number( i - 1)) + str('Element') + str(el) if pos not in def_str: def_str.append(pos) print(len(def_str)) return def_str
def cif(src="POSCAR"): """ cifファイルを作成 """ srcpos = Poscar.from_file(src) finder = SpacegroupAnalyzer(srcpos.structure) std = finder.get_conventional_standard_structure() cif = CifWriter(std, find_spacegroup=True, symprec=0.1) cif.write_file("poscar.cif")
def smart_vac(strt=None,parameters=None): parameters['control_file']='/users/knc6/inelast_nobox.mod' vac_arr=[] sg_mat = SpacegroupAnalyzer(strt) mat_cvn = sg_mat.get_conventional_standard_structure() mat_cvn.sort() cellmax=2 #int(mat_cvn.composition.num_atoms)+int(mat_cvn.ntypesp)#5 ase_atoms = AseAtomsAdaptor().get_atoms(mat_cvn) ase_atoms=ase_atoms*(cellmax,cellmax,cellmax) #print ("type of trt is= celmmax",type(strt),cellmax) try: print ("int(strt.composition.num_atoms)",int(strt.composition.num_atoms)) print (int(strt.ntypesp)) except: pass vac_done=0 tol=0.01 #change 0.1 try: vac=vac_antisite_def_struct_gen(cellmax=cellmax,struct=strt) except: print ("Failed for v_1",os.getcwd()) pass def_list=[100000 for y in range(len(vac)-1)] while vac_done !=1: try: vac=vac_antisite_def_struct_gen(cellmax=cellmax,struct=strt) except: print ("Failed for v_2",os.getcwd()) pass if vac not in vac_arr: try: vac_arr.append(vac) print ("in smart_vac(strt=None), cellmax,vac,vac_done=",cellmax,vac,vac_done) def_list2,header_list=def_energy(vac=vac,parameters=parameters) diff=matrix(def_list)-matrix(def_list2) diff_arr=np.array(diff).flatten() print ("in smart_vac(strt=None diff_arr=",diff_arr) print ("sleepig for 5") except: print ("Failed for v_3",os.getcwd()) pass if any(diff_arr)>tol: # for el in diff_arr: # if abs(el)>tol : # print ("in smart_vac(strt=None abs_el=",abs(el)) vac_done=0 cellmax=cellmax+1 ase_atoms = AseAtomsAdaptor().get_atoms(mat_cvn) ase_atoms=ase_atoms*(cellmax,cellmax,cellmax) if len(ase_atoms) >50: vac_done=1 break def_list=def_list2 else: vac_done=1 return def_list,header_list
def prim_cif(self, dst): """ primitive cellでのcifフォーマットをgetする """ finder = SpacegroupAnalyzer(self.structure) structure = finder.get_primitive_standard_structure() structure = finder.get_conventional_standard_structure() cif = CifWriter(structure, symprec=0.1) cif.write_file(dst)
def cif(src): """ cifファイルを作成 """ srcpos = Poscar.from_file(src) finder = SpacegroupAnalyzer(srcpos.structure) std_str = finder.get_conventional_standard_structure() std_cif = CifWriter(std_str, symprec=0.1) std_cif.write_file("poscar.cif")
def geo_table_row(cl=None, st=None, name='', show_angle=0, mnpo4_hack=False): #return list of geo data for cl, which can be used for table """ mnpo4_hack (bool) - if true exchange a and c for mnpo4 phase """ if cl: st = cl.end # if not name: if header.pymatgen_flag: spg = st.get_space_group_info() spg = latex_spg(spg[0]) #transform to standard st_mp = st.convert2pymatgen() symprec = 0.1 sf = SpacegroupAnalyzer(st_mp, symprec=symprec) st_mp_prim = sf.find_primitive() st_mp_conv = sf.get_conventional_standard_structure() else: spg = '-' # st_mp_prim. # print('primitive,', st_mp_prim.lattice) # print('conventio,', st_mp_conv.lattice) # st_mp_prim = sf.get_primitive_standard_structure() # st_mp_prim = sf.get_conventional_standard_structure() if not name: # print(dir(st_mp )) # st.printme() name = st.get_reduced_formula() name = latex_chem(name) alpha, beta, gamma = st.get_angles() elem = np.array(st.get_elements()) if 'a' in show_angle: angle = '& {:5.2f}'.format(alpha) elif 'b' in show_angle: angle = '& {:5.2f}'.format(beta) elif 'g' in show_angle: angle = '& {:5.2f}'.format(gamma) else: angle = '' v = st.vlength a, b, c = v if mnpo4_hack and 'MnPO' in name: c, b, a = v return '{:15s} &{:s} & {:5.2f} & {:5.2f} & {:5.2f} '.format( name, 'DFT+U', a, b, c) + angle + '& {:5.1f} & {:s}'.format( st.vol, spg)
def cif(src='POSCAR'): """ cifファイルを作成 """ srcpos = Poscar.from_file(src) finder = SpacegroupAnalyzer(srcpos.structure) std_str = finder.get_conventional_standard_structure() cif_obj = CifWriter(std_str, symprec=0.1) cif_obj.write_file('poscar.cif')
def pmg_surfer(mpid='', vacuum=15, mat=None, max_index=1, min_slab_size=15): if mat == None: with MPRester() as mp: mat = mp.get_structure_by_material_id(mpid) if mpid == '': print('Provide structure') sg_mat = SpacegroupAnalyzer(mat) mat_cvn = sg_mat.get_conventional_standard_structure() mat_cvn.sort() indices = get_symmetrically_distinct_miller_indices(mat_cvn, max_index) #ase_atoms = AseAtomsAdaptor().get_atoms(mat_cvn) structures = [] pos = Poscar(mat_cvn) try: pos.comment = str('sbulk') + str('@') + str('vac') + str(vacuum) + str( '@') + str('size') + str(min_slab_size) except: pass structures.append(pos) mat_cvn.to(fmt='poscar', filename=str('POSCAR-') + str('cvn') + str('.vasp')) for i in indices: slab = SlabGenerator(initial_structure=mat_cvn, miller_index=i, min_slab_size=min_slab_size, min_vacuum_size=vacuum, lll_reduce=False, center_slab=True, primitive=False).get_slab() normal_slab = slab.get_orthogonal_c_slab() slab_pymatgen = Poscar(normal_slab).structure #ase_slab.center(vacuum=vacuum, axis=2) #slab_pymatgen = AseAtomsAdaptor().get_structure(ase_slab) xy_size = min_slab_size dim1 = int((float(xy_size) / float(max(abs(slab_pymatgen.lattice.matrix[0]))))) + 1 dim2 = int( float(xy_size) / float(max(abs(slab_pymatgen.lattice.matrix[1])))) + 1 slab_pymatgen.make_supercell([dim1, dim2, 1]) slab_pymatgen.sort() surf_name = '_'.join(map(str, i)) pos = Poscar(slab_pymatgen) try: pos.comment = str("Surf-") + str(surf_name) + str('@') + str( 'vac') + str(vacuum) + str('@') + str('size') + str( min_slab_size) except: pass pos.write_file(filename=str('POSCAR-') + str("Surf-") + str(surf_name) + str('.vasp')) structures.append(pos) return structures
def get_conventionalstructure(ase_structure): from pymatgen.io.ase import AseAtomsAdaptor from pymatgen.symmetry.analyzer import SpacegroupAnalyzer mg_structure = AseAtomsAdaptor.get_structure(ase_structure) sga = SpacegroupAnalyzer(mg_structure) standard_structure = sga.get_conventional_standard_structure() standard_ase = AseAtomsAdaptor.get_atoms(standard_structure) return standard_ase
def setUpClass(cls): si_struct = cls.get_structure('Si') sio2_struct = cls.get_structure('SiO2') sga = SpacegroupAnalyzer(si_struct) si_conventional = sga.get_conventional_standard_structure() sga = SpacegroupAnalyzer(sio2_struct) sio2_conventional = sga.get_conventional_standard_structure() cls.ibs = [] cls.ibs.append( cls.make_ib(si_conventional, sio2_conventional, [1, 0, 0])) cls.ibs.append( cls.make_ib(sio2_conventional, si_conventional, [1, 0, 0])) cls.ibs.append(cls.make_ib(si_struct, sio2_struct, [1, 0, 0])) cls.ibs.append(cls.make_ib(sio2_struct, si_struct, [1, 0, 0])) cls.ibs.append(cls.make_ib(si_struct, sio2_struct, [1, 1, 1])) cls.ibs.append(cls.make_ib(sio2_struct, si_struct, [1, 1, 1]))
def as_dict(self): """ Returns the CTRL as a dictionary. "SITE" and "CLASS" are of the form {'CATEGORY': {'TOKEN': value}}, the rest is of the form 'TOKEN'/'CATEGORY': value. It gets the conventional standard structure because primitive cells use the conventional a-lattice parameter as the scaling factor and not the a-lattice parameter of the primitive cell. """ ctrl_dict = { "@module": self.__class__.__module__, "@class": self.__class__.__name__, } if self.header is not None: ctrl_dict["HEADER"] = self.header if self.version is not None: ctrl_dict["VERS"] = self.version sga = SpacegroupAnalyzer(self.structure) alat = sga.get_conventional_standard_structure().lattice.a plat = self.structure.lattice.matrix / alat """ The following is to find the classes (atoms that are not symmetry equivalent, and create labels. Note that LMTO only attaches numbers with the second atom of the same species, e.g. "Bi", "Bi1", "Bi2", etc. """ eq_atoms = sga.get_symmetry_dataset()["equivalent_atoms"] ineq_sites_index = list(set(eq_atoms)) sites = [] classes = [] num_atoms = {} for s, site in enumerate(self.structure.sites): atom = site.specie label_index = ineq_sites_index.index(eq_atoms[s]) if atom.symbol in num_atoms: if label_index + 1 > sum(num_atoms.values()): num_atoms[atom.symbol] += 1 atom_label = atom.symbol + str(num_atoms[atom.symbol] - 1) classes.append({"ATOM": atom_label, "Z": atom.Z}) else: num_atoms[atom.symbol] = 1 classes.append({"ATOM": atom.symbol, "Z": atom.Z}) sites.append({"ATOM": classes[label_index]["ATOM"], "POS": site.coords / alat}) ctrl_dict.update( { "ALAT": alat / bohr_to_angstrom, "PLAT": plat, "CLASS": classes, "SITE": sites, } ) return ctrl_dict
def standard(src="POSCAR"): """ standardに変換 """ srcpos = Poscar.from_file(src) finder = SpacegroupAnalyzer(srcpos.structure) std = finder.get_conventional_standard_structure() dstpos = Poscar(std) dst = "POSCAR_std" Cabinet.reserve_file(dst) dstpos.write_file(dst)
def std(src='POSCAR'): """ conventional standard cell に変換 """ srcpos = Poscar.from_file(src) finder = SpacegroupAnalyzer(srcpos.structure) std_str = finder.get_conventional_standard_structure() dstpos = Poscar(std_str) dst = 'POSCAR_std' Cabinet.reserve_file(dst) dstpos.write_file(dst)
def convert(bulk, slab, index, output, generate=True, print_M=True): primitiveCell = mg.Structure.from_file(bulk) refSlab = mg.Structure.from_file(slab) sa = SpacegroupAnalyzer(primitiveCell) conventionalCell = sa.get_conventional_standard_structure() conventionalCell.to(filename='POSCAR.conventional') bulk = read('POSCAR.conventional') os.remove('POSCAR.conventional') slab = surface(bulk, index, layers=2, vacuum=10) lattice, _, _ = spglib.standardize_cell(cell=(slab.get_cell(), slab.get_scaled_positions(), slab.get_atomic_numbers()), no_idealize=True) lattice_params = np.sort(np.linalg.norm(lattice, axis=1))[:2] scales = np.round( np.array([refSlab.lattice.a, refSlab.lattice.b] / lattice_params), 2) newLattice = [] oldLattice = refSlab.lattice for length, scale in zip([oldLattice.a, oldLattice.b], scales): for j in range(len(lattice)): if abs((np.linalg.norm(lattice[j]) * scale) - length) < 1e-1: newLattice.append(copy.copy(scale * lattice[j][:])) lattice[j] = [0, 0, 0] break for i in range(len(lattice)): norm = np.linalg.norm(lattice[i]) if norm > 1e-1: newLattice.append(lattice[i] / norm * oldLattice.c) break newLattice = Lattice(np.array(newLattice)) for x, y in zip(oldLattice.angles, newLattice.angles): assert abs( x - y ) < 1e-2, "The converted lattice has incorrect angles: {} compared with reference slab: {}".format( " ".join(str(x) for x in newLattice.angles), " ".join(str(x) for x in oldLattice.angles)) newSlab = Structure(newLattice, [], []) for atom in refSlab: newSlab.append(atom.specie, atom.frac_coords[:]) if generate: Poscar(newSlab.get_sorted_structure()).write_file(output, direct=True) transformMat = newSlab.lattice.matrix.dot( np.linalg.inv(primitiveCell.lattice.matrix)) transformMat = transformMat.round().astype(int) if print_M: print('-------------------------------------------') print('Your Transformtaion Matrix is:') print(' ') print(transformMat) print('-------------------------------------------') return transformMat
def surfer(vacuum=15, layers=2, mat=None, max_index=1, write_file=True): """ ASE surface bulder Args: vacuum: vacuum region mat: Structure object max_index: maximum miller index min_slab_size: minimum slab size Returns: structures: list of surface Structure objects """ if mat == None: print("Provide structure") sg_mat = SpacegroupAnalyzer(mat) mat_cvn = sg_mat.get_conventional_standard_structure() mat_cvn.sort() indices = get_symmetrically_distinct_miller_indices(mat_cvn, max_index) ase_atoms = AseAtomsAdaptor().get_atoms(mat_cvn) structures = [] pos = Poscar(mat_cvn) try: pos.comment = (str("sbulk") + str("@") + str("vac") + str(vacuum) + str("@") + str("layers") + str(layers)) except: pass structures.append(pos) if write_file == True: mat_cvn.to(fmt="poscar", filename=str("POSCAR-") + str("cvn") + str(".vasp")) for i in indices: ase_slab = surface(ase_atoms, i, layers) ase_slab.center(vacuum=vacuum, axis=2) slab_pymatgen = AseAtomsAdaptor().get_structure(ase_slab) slab_pymatgen.sort() surf_name = "_".join(map(str, i)) pos = Poscar(slab_pymatgen) try: pos.comment = (str("Surf-") + str(surf_name) + str("@") + str("vac") + str(vacuum) + str("@") + str("layers") + str(layers)) except: pass if write_file == True: pos.write_file(filename=str("POSCAR-") + str("Surf-") + str(surf_name) + str(".vasp")) structures.append(pos) return structures
def apply_transformation(self, structure): """ Returns most primitive cell for structure. Args: structure: A structure Returns: The same structure in a conventional standard setting """ sga = SpacegroupAnalyzer(structure, symprec=self.symprec, angle_tolerance=self.angle_tolerance) return sga.get_conventional_standard_structure(international_monoclinic=self.international_monoclinic)
def apply_transformation(self, structure): """ Returns most primitive cell for structure. Args: structure: A structure Returns: The same structure in a conventional standard setting """ sga = SpacegroupAnalyzer(structure, symprec=self.symprec, angle_tolerance=self.angle_tolerance) return sga.get_conventional_standard_structure(international_monoclinic=self.international_monoclinic)
def step1(): """ get substrate bulk structures from materialsproject for Pt, Ag, Cu, Ni, Al, Au, Pd, Ir and do 3d relaxation(ISIF=3) get 2d structures from the provided poscars(just poscar_graphene) and relax in x and y only(vasp_noz bin) - POSCAR_graphene must be made available in the directory - creates required input files and submits the jobs to the que - 8 + 1 jobs - returns: step1_sub.json step1_2d.json """ #job directory for the runs job_dir_sub = 'step1_sub' job_dir_2d = 'step1_2d' # create list of all substrate poscars poscars_sub = [] poscars_2d = [] # substrate structures for sub in substrates: struct_sub = get_struct_from_mp(sub) sa_sub = SpacegroupAnalyzer(struct_sub) struct_sub = sa_sub.get_conventional_standard_structure() poscars_sub.append(Poscar(struct_sub)) # 2d structures for td in mat2ds: poscars_2d.append(Poscar.from_file(td)) # setup calibrate and run'em turn_knobs_sub = OrderedDict( [ ('POSCAR', poscars_sub) ]) turn_knobs_2d = OrderedDict( [ ('POSCAR', poscars_2d) ]) # normal binary qadapter_sub, job_cmd_sub = get_run_cmmnd(nnodes=nnodes, nprocs=nprocs, walltime=walltime, job_bin=bin_sub, mem=mem) # binary with z constraint qadapter_2d, job_cmd_2d = get_run_cmmnd(nnodes=nnodes, nprocs=nprocs, walltime=walltime, job_bin=bin_2d, mem=mem) run_cal(turn_knobs_sub, qadapter_sub, job_cmd_sub, job_dir_sub, 'step1_sub', incar=incar_sub, kpoints=kpoints_sub) run_cal(turn_knobs_2d, qadapter_2d, job_cmd_2d, job_dir_2d, 'step1_2d', incar=incar_2d, kpoints=kpoints_2d) return ['step1_sub.json', 'step1_2d.json']
def _get_data_from_single_dirc(dirc, src_str="str_relax.out", src_ene='energy'): """ 指定したdircから構造とエネルギーを読み取る """ src = os.path.join(dirc, src_str) strout = StrOut.from_file(src) src = os.path.join(dirc, src_ene) with open(src, 'r') as rfile: lines = rfile.readlines() num_atoms = sum(strout.structure.composition. to_data_dict['unit_cell_composition'].values()) energy = float(lines[0]) / num_atoms analyzer = SpacegroupAnalyzer(strout.structure) #std_prim = analyzer.get_primitive_standard_structure() std_str = analyzer.get_conventional_standard_structure() analyzer = SpacegroupAnalyzer(std_str) wyckoffs = analyzer.get_symmetry_dataset()['wyckoffs'] formula = std_str.composition.to_data_dict['unit_cell_composition'] symbol_spg = analyzer.get_spacegroup_symbol() num_spg = analyzer.get_spacegroup_number() spg = [symbol_spg, num_spg] lattice = std_str.as_dict()['lattice'] equiv_sites = analyzer.get_symmetrized_structure().equivalent_sites equiv_indices = analyzer.get_symmetrized_structure().equivalent_indices # Wycoffs labelと組み合わせたsites_groupのlistを作る sites_and_wyckoffs = [] for eq_s, eq_i in zip(equiv_sites, equiv_indices): sites_and_wyckoffs.append({'wyckoffs': wyckoffs[eq_i[0]], 'site_grp': eq_s}) # check for i in range(len(eq_i)-1): if wyckoffs[eq_i[i]] != wyckoffs[eq_i[i+1]] or \ len(eq_s) != len(eq_i): print("wyckoffs label is wrong !!!!") print(wyckoffs) print(eq_i) print(len(eq_s)) print(dirc) exit() return {'formula': formula, 'lattice': lattice, 'spg': spg, 'sites_and_wyckoffs': sites_and_wyckoffs, 'energy': energy, 'str_id': os.path.basename(dirc)}
def get_smallest_expansion(structure : Structure, length : float): """ Finds the smallest expansion of the provided cell such that all sides are at minimum length. Will change shape of cell if it creates a better match :param structure: Unit cell to convert :param length: Minimum vector difference :return: """ from pymatgen.symmetry.analyzer import SpacegroupAnalyzer sga = SpacegroupAnalyzer(structure) structures = [] structures.append(structure) try: structures.append(structure.get_primitive_structure()) except: pass try: structures.append(structure.get_reduced_structure('niggli')) except: pass try: structures.append(structure.get_reduced_structure('LLL')) except: pass try: structures.append(sga.get_conventional_standard_structure()) except: pass try: structures.append(sga.get_primitive_standard_structure()) except: pass best_structure = None for s in structures: # type: Structure l = s.lattice expansion = [ ceil(length / vec) for vec in l.abc ] possible_structure = s * expansion if best_structure == None or len(possible_structure) < len(best_structure): best_structure = possible_structure if structure.site_properties and not best_structure.site_properties: def get_property(prop, atom): i = structure.species.index(atom) return structure.site_properties[prop][i] site_properties = { prop : [ get_property(prop, atom) for atom in best_structure.species ] for prop in structure.site_properties} best_structure = Structure(best_structure.lattice, best_structure.species, best_structure.frac_coords, site_properties=site_properties) return best_structure
def handle_subcommand_test_relax(args): default_commands = { 'lammps': 'lammps' } command = args.command if args.command else default_commands.get(args.software) predict = Predict(calculator=args.software, command=command, num_workers=1) potential = Potential.from_file(args.potential) structure = get_structure(args.structure) import warnings warnings.filterwarnings("ignore") # yes I have sinned sga = SpacegroupAnalyzer(structure) conventional_structure = sga.get_conventional_standard_structure() old_lattice, new_lattice = predict.lattice_constant(conventional_structure, potential) equilibrium_structure = Structure( new_lattice, [s.specie.element for s in conventional_structure.sites], [s.frac_coords for s in conventional_structure.sites]) equilibrium_structure.to(filename=args.output_filename)
from pymatgen.io.vasp.inputs import Poscar from mpinterfaces.calibrate import CalibrateSlab from mpinterfaces import get_struct_from_mp from mpinterfaces.interface import Interface from mpinterfaces.transformations import * from mpinterfaces.utils import * separation = 3 # in angstroms nlayers_2d = 2 nlayers_substrate = 2 substrate_bulk = Structure.from_file('POSCAR_substrate') # substrate_bulk = get_struct_from_mp('Ag') sa_sub = SpacegroupAnalyzer(substrate_bulk) substrate_bulk = sa_sub.get_conventional_standard_structure() substrate_slab = Interface(substrate_bulk, hkl=[1, 1, 1], min_thick=10, min_vac=25, primitive=False, from_ase=True) # substrate_slab = slab_from_file([0,0,1], 'POSCAR_substrate') mat2d_slab = slab_from_file([0, 0, 1], 'POSCAR_2D') # get the in-plane lattice aligned slabs # substrate_slab.to(fmt='poscar', filename='POSCAR_substrate_slab.vasp') mat2d_slab.to(fmt='poscar', filename='POSCAR_mat2d_slab.vasp') # selective dynamics flag sd_flags = CalibrateSlab.set_sd_flags( interface=substrate_slab, n_layers=nlayers_substrate, top=True, bottom=False)
def get_grain_boundary_interface(structure=None, hkl_pair= {'hkl': [[1,0,0],[1,1,0]],\ 'thickness':[10,10]}, twist = 0, tilt = 0, separation=0): """ Args: structure: pymatgen structure to create grain boundary in hkl_pair: dict of {'hkl':thickness} twist: twist in degrees tilt: tilt in degrees """ structure = get_struct_from_mp(structure, MAPI_KEY="") sa = SpacegroupAnalyzer(structure) structure_conventional = sa.get_conventional_standard_structure() structure = structure_conventional.copy() structure.sort() #creation of lower part of grain boundary lower= Interface(structure,\ hkl = hkl_pair['hkl'][0], min_thick = hkl_pair['thickness'][0], min_vac = separation+hkl_pair['thickness'][1], primitive = False, from_ase = True, center_slab=False) lower.to(fmt="poscar", filename="POSCAR_lower.vasp") #creation of upper part of grain boundary upper= Interface(structure,\ hkl = hkl_pair['hkl'][1], min_thick = hkl_pair['thickness'][1], min_vac = 0, primitive = False, from_ase = True) #find top atoms reference of lower part of gb substrate_top_z = np.max(np.array([site.coords for site in lower])[:,2]) # define twist and tilt vectors twist_shift_normal = lower.lattice.matrix[2,:]/\ np.linalg.norm(lower.lattice.matrix[2,:]) tilt_normal = upper.lattice.matrix[1,:]/\ np.linalg.norm(upper.lattice.matrix[2,:]) #define twist operation SymmOp object twist_op = SymmOp.from_axis_angle_and_translation(axis= twist_shift_normal,\ angle=twist, angle_in_radians=False,translation_vec=(0, 0, 0)) #define tilt operation SymmOp object tilt_op = SymmOp.from_axis_angle_and_translation(axis= tilt_normal,\ angle=tilt, angle_in_radians=False,translation_vec=(0, 0, 0)) upper.apply_operation(twist_op) upper.to(fmt="poscar", filename="POSCAR_upper.vasp") upper.apply_operation(tilt_op) #define shift separation along twist vector normal to upper plane shift = -1*twist_shift_normal/np.linalg.norm(twist_shift_normal) * separation #define origin to shift w.r.t top of the lower grain origin = np.array([0,0, substrate_top_z]) #shift sites in upper for site in upper: new_coords = site.coords - origin + shift lower.append(site.specie, new_coords, coords_are_cartesian=True) return lower
class HighSymmKpath(object): """ This class looks for path along high symmetry lines in the Brillouin Zone. It is based on Setyawan, W., & Curtarolo, S. (2010). High-throughput electronic band structure calculations: Challenges and tools. Computational Materials Science, 49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010 It should be used with primitive structures that comply with the definition from the paper. The symmetry is determined by spglib through the SpacegroupAnalyzer class. The analyzer can be used to produce the correct primitive structure (method get_primitive_standard_structure(international_monoclinic=False)). A warning will signal possible compatibility problems with the given structure. Args: structure (Structure): Structure object symprec (float): Tolerance for symmetry finding angle_tolerance (float): Angle tolerance for symmetry finding. atol (float): Absolute tolerance used to compare the input structure with the one expected as primitive standard. A warning will be issued if the lattices don't match. """ def __init__(self, structure, symprec=0.01, angle_tolerance=5, atol=1e-8): self._structure = structure self._sym = SpacegroupAnalyzer(structure, symprec=symprec, angle_tolerance=angle_tolerance) self._prim = self._sym\ .get_primitive_standard_structure(international_monoclinic=False) self._conv = self._sym.get_conventional_standard_structure(international_monoclinic=False) self._prim_rec = self._prim.lattice.reciprocal_lattice self._kpath = None #Note: this warning will be issued for space groups 38-41, since the primitive cell must be #reformatted to match Setyawan/Curtarolo convention in order to work with the current k-path #generation scheme. if not np.allclose(self._structure.lattice.matrix, self._prim.lattice.matrix, atol=atol): warnings.warn("The input structure does not match the expected standard primitive! " "The path can be incorrect. Use at your own risk.") lattice_type = self._sym.get_lattice_type() spg_symbol = self._sym.get_space_group_symbol() if lattice_type == "cubic": if "P" in spg_symbol: self._kpath = self.cubic() elif "F" in spg_symbol: self._kpath = self.fcc() elif "I" in spg_symbol: self._kpath = self.bcc() else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "tetragonal": if "P" in spg_symbol: self._kpath = self.tet() elif "I" in spg_symbol: a = self._conv.lattice.abc[0] c = self._conv.lattice.abc[2] if c < a: self._kpath = self.bctet1(c, a) else: self._kpath = self.bctet2(c, a) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "orthorhombic": a = self._conv.lattice.abc[0] b = self._conv.lattice.abc[1] c = self._conv.lattice.abc[2] if "P" in spg_symbol: self._kpath = self.orc() elif "F" in spg_symbol: if 1 / a ** 2 > 1 / b ** 2 + 1 / c ** 2: self._kpath = self.orcf1(a, b, c) elif 1 / a ** 2 < 1 / b ** 2 + 1 / c ** 2: self._kpath = self.orcf2(a, b, c) else: self._kpath = self.orcf3(a, b, c) elif "I" in spg_symbol: self._kpath = self.orci(a, b, c) elif "C" in spg_symbol or "A" in spg_symbol: self._kpath = self.orcc(a, b, c) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "hexagonal": self._kpath = self.hex() elif lattice_type == "rhombohedral": alpha = self._prim.lattice.lengths_and_angles[1][0] if alpha < 90: self._kpath = self.rhl1(alpha * pi / 180) else: self._kpath = self.rhl2(alpha * pi / 180) elif lattice_type == "monoclinic": a, b, c = self._conv.lattice.abc alpha = self._conv.lattice.lengths_and_angles[1][0] #beta = self._conv.lattice.lengths_and_angles[1][1] if "P" in spg_symbol: self._kpath = self.mcl(b, c, alpha * pi / 180) elif "C" in spg_symbol: kgamma = self._prim_rec.lengths_and_angles[1][2] if kgamma > 90: self._kpath = self.mclc1(a, b, c, alpha * pi / 180) if kgamma == 90: self._kpath = self.mclc2(a, b, c, alpha * pi / 180) if kgamma < 90: if b * cos(alpha * pi / 180) / c\ + b ** 2 * sin(alpha * pi / 180) ** 2 / a ** 2 < 1: self._kpath = self.mclc3(a, b, c, alpha * pi / 180) if b * cos(alpha * pi / 180) / c \ + b ** 2 * sin(alpha * pi / 180) ** 2 / a ** 2 == 1: self._kpath = self.mclc4(a, b, c, alpha * pi / 180) if b * cos(alpha * pi / 180) / c \ + b ** 2 * sin(alpha * pi / 180) ** 2 / a ** 2 > 1: self._kpath = self.mclc5(a, b, c, alpha * pi / 180) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "triclinic": kalpha = self._prim_rec.lengths_and_angles[1][0] kbeta = self._prim_rec.lengths_and_angles[1][1] kgamma = self._prim_rec.lengths_and_angles[1][2] if kalpha > 90 and kbeta > 90 and kgamma > 90: self._kpath = self.tria() if kalpha < 90 and kbeta < 90 and kgamma < 90: self._kpath = self.trib() if kalpha > 90 and kbeta > 90 and kgamma == 90: self._kpath = self.tria() if kalpha < 90 and kbeta < 90 and kgamma == 90: self._kpath = self.trib() else: warn("Unknown lattice type %s" % lattice_type) @property def structure(self): """ Returns: The standardized primitive structure """ return self._prim @property def conventional(self): """ Returns: The conventional cell structure """ return self._conv @property def prim(self): """ Returns: The primitive cell structure """ return self._prim @property def prim_rec(self): """ Returns: The primitive reciprocal cell structure """ return self._prim_rec @property def kpath(self): """ Returns: The symmetry line path in reciprocal space """ return self._kpath def get_kpoints(self, line_density=20, coords_are_cartesian=True): """ Returns: the kpoints along the paths in cartesian coordinates together with the labels for symmetry points -Wei """ list_k_points = [] sym_point_labels = [] for b in self.kpath['path']: for i in range(1, len(b)): start = np.array(self.kpath['kpoints'][b[i - 1]]) end = np.array(self.kpath['kpoints'][b[i]]) distance = np.linalg.norm( self._prim_rec.get_cartesian_coords(start) - self._prim_rec.get_cartesian_coords(end)) nb = int(ceil(distance * line_density)) sym_point_labels.extend([b[i - 1]] + [''] * (nb - 1) + [b[i]]) list_k_points.extend( [self._prim_rec.get_cartesian_coords(start) + float(i) / float(nb) * (self._prim_rec.get_cartesian_coords(end) - self._prim_rec.get_cartesian_coords(start)) for i in range(0, nb + 1)]) if coords_are_cartesian: return list_k_points, sym_point_labels else: frac_k_points = [self._prim_rec.get_fractional_coords(k) for k in list_k_points] return frac_k_points, sym_point_labels def cubic(self): self.name = "CUB" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'X': np.array([0.0, 0.5, 0.0]), 'R': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.5, 0.0])} path = [["\\Gamma", "X", "M", "\\Gamma", "R", "X"], ["M", "R"]] return {'kpoints': kpoints, 'path': path} def fcc(self): self.name = "FCC" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'K': np.array([3.0 / 8.0, 3.0 / 8.0, 3.0 / 4.0]), 'L': np.array([0.5, 0.5, 0.5]), 'U': np.array([5.0 / 8.0, 1.0 / 4.0, 5.0 / 8.0]), 'W': np.array([0.5, 1.0 / 4.0, 3.0 / 4.0]), 'X': np.array([0.5, 0.0, 0.5])} path = [["\\Gamma", "X", "W", "K", "\\Gamma", "L", "U", "W", "L", "K"], ["U", "X"]] return {'kpoints': kpoints, 'path': path} def bcc(self): self.name = "BCC" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'H': np.array([0.5, -0.5, 0.5]), 'P': np.array([0.25, 0.25, 0.25]), 'N': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "H", "N", "\\Gamma", "P", "H"], ["P", "N"]] return {'kpoints': kpoints, 'path': path} def tet(self): self.name = "TET" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.5, 0.0]), 'R': np.array([0.0, 0.5, 0.5]), 'X': np.array([0.0, 0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "X", "M", "\\Gamma", "Z", "R", "A", "Z"], ["X", "R"], ["M", "A"]] return {'kpoints': kpoints, 'path': path} def bctet1(self, c, a): self.name = "BCT1" eta = (1 + c ** 2 / a ** 2) / 4.0 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'M': np.array([-0.5, 0.5, 0.5]), 'N': np.array([0.0, 0.5, 0.0]), 'P': np.array([0.25, 0.25, 0.25]), 'X': np.array([0.0, 0.0, 0.5]), 'Z': np.array([eta, eta, -eta]), 'Z_1': np.array([-eta, 1 - eta, eta])} path = [["\\Gamma", "X", "M", "\\Gamma", "Z", "P", "N", "Z_1", "M"], ["X", "P"]] return {'kpoints': kpoints, 'path': path} def bctet2(self, c, a): self.name = "BCT2" eta = (1 + a ** 2 / c ** 2) / 4.0 zeta = a ** 2 / (2 * c ** 2) kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'N': np.array([0.0, 0.5, 0.0]), 'P': np.array([0.25, 0.25, 0.25]), '\\Sigma': np.array([-eta, eta, eta]), '\\Sigma_1': np.array([eta, 1 - eta, -eta]), 'X': np.array([0.0, 0.0, 0.5]), 'Y': np.array([-zeta, zeta, 0.5]), 'Y_1': np.array([0.5, 0.5, -zeta]), 'Z': np.array([0.5, 0.5, -0.5])} path = [["\\Gamma", "X", "Y", "\\Sigma", "\\Gamma", "Z", "\\Sigma_1", "N", "P", "Y_1", "Z"], ["X", "P"]] return {'kpoints': kpoints, 'path': path} def orc(self): self.name = "ORC" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'R': np.array([0.5, 0.5, 0.5]), 'S': np.array([0.5, 0.5, 0.0]), 'T': np.array([0.0, 0.5, 0.5]), 'U': np.array([0.5, 0.0, 0.5]), 'X': np.array([0.5, 0.0, 0.0]), 'Y': np.array([0.0, 0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "X", "S", "Y", "\\Gamma", "Z", "U", "R", "T", "Z"], ["Y", "T"], ["U", "X"], ["S", "R"]] return {'kpoints': kpoints, 'path': path} def orcf1(self, a, b, c): self.name = "ORCF1" zeta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 eta = (1 + a ** 2 / b ** 2 + a ** 2 / c ** 2) / 4 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5 + zeta, zeta]), 'A_1': np.array([0.5, 0.5 - zeta, 1 - zeta]), 'L': np.array([0.5, 0.5, 0.5]), 'T': np.array([1, 0.5, 0.5]), 'X': np.array([0.0, eta, eta]), 'X_1': np.array([1, 1 - eta, 1 - eta]), 'Y': np.array([0.5, 0.0, 0.5]), 'Z': np.array([0.5, 0.5, 0.0])} path = [["\\Gamma", "Y", "T", "Z", "\\Gamma", "X", "A_1", "Y"], ["T", "X_1"], ["X", "A", "Z"], ["L", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def orcf2(self, a, b, c): self.name = "ORCF2" phi = (1 + c ** 2 / b ** 2 - c ** 2 / a ** 2) / 4 eta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 delta = (1 + b ** 2 / a ** 2 - b ** 2 / c ** 2) / 4 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'C': np.array([0.5, 0.5 - eta, 1 - eta]), 'C_1': np.array([0.5, 0.5 + eta, eta]), 'D': np.array([0.5 - delta, 0.5, 1 - delta]), 'D_1': np.array([0.5 + delta, 0.5, delta]), 'L': np.array([0.5, 0.5, 0.5]), 'H': np.array([1 - phi, 0.5 - phi, 0.5]), 'H_1': np.array([phi, 0.5 + phi, 0.5]), 'X': np.array([0.0, 0.5, 0.5]), 'Y': np.array([0.5, 0.0, 0.5]), 'Z': np.array([0.5, 0.5, 0.0])} path = [["\\Gamma", "Y", "C", "D", "X", "\\Gamma", "Z", "D_1", "H", "C"], ["C_1", "Z"], ["X", "H_1"], ["H", "Y"], ["L", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def orcf3(self, a, b, c): self.name = "ORCF3" zeta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 eta = (1 + a ** 2 / b ** 2 + a ** 2 / c ** 2) / 4 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5 + zeta, zeta]), 'A_1': np.array([0.5, 0.5 - zeta, 1 - zeta]), 'L': np.array([0.5, 0.5, 0.5]), 'T': np.array([1, 0.5, 0.5]), 'X': np.array([0.0, eta, eta]), 'X_1': np.array([1, 1 - eta, 1 - eta]), 'Y': np.array([0.5, 0.0, 0.5]), 'Z': np.array([0.5, 0.5, 0.0])} path = [["\\Gamma", "Y", "T", "Z", "\\Gamma", "X", "A_1", "Y"], ["X", "A", "Z"], ["L", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def orci(self, a, b, c): self.name = "ORCI" zeta = (1 + a ** 2 / c ** 2) / 4 eta = (1 + b ** 2 / c ** 2) / 4 delta = (b ** 2 - a ** 2) / (4 * c ** 2) mu = (a ** 2 + b ** 2) / (4 * c ** 2) kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'L': np.array([-mu, mu, 0.5 - delta]), 'L_1': np.array([mu, -mu, 0.5 + delta]), 'L_2': np.array([0.5 - delta, 0.5 + delta, -mu]), 'R': np.array([0.0, 0.5, 0.0]), 'S': np.array([0.5, 0.0, 0.0]), 'T': np.array([0.0, 0.0, 0.5]), 'W': np.array([0.25, 0.25, 0.25]), 'X': np.array([-zeta, zeta, zeta]), 'X_1': np.array([zeta, 1 - zeta, -zeta]), 'Y': np.array([eta, -eta, eta]), 'Y_1': np.array([1 - eta, eta, -eta]), 'Z': np.array([0.5, 0.5, -0.5])} path = [["\\Gamma", "X", "L", "T", "W", "R", "X_1", "Z", "\\Gamma", "Y", "S", "W"], ["L_1", "Y"], ["Y_1", "Z"]] return {'kpoints': kpoints, 'path': path} def orcc(self, a, b, c): self.name = "ORCC" zeta = (1 + a ** 2 / b ** 2) / 4 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([zeta, zeta, 0.5]), 'A_1': np.array([-zeta, 1 - zeta, 0.5]), 'R': np.array([0.0, 0.5, 0.5]), 'S': np.array([0.0, 0.5, 0.0]), 'T': np.array([-0.5, 0.5, 0.5]), 'X': np.array([zeta, zeta, 0.0]), 'X_1': np.array([-zeta, 1 - zeta, 0.0]), 'Y': np.array([-0.5, 0.5, 0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "X", "S", "R", "A", "Z", "\\Gamma", "Y", "X_1", "A_1", "T", "Y"], ["Z", "T"]] return {'kpoints': kpoints, 'path': path} def hex(self): self.name = "HEX" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.0, 0.0, 0.5]), 'H': np.array([1.0 / 3.0, 1.0 / 3.0, 0.5]), 'K': np.array([1.0 / 3.0, 1.0 / 3.0, 0.0]), 'L': np.array([0.5, 0.0, 0.5]), 'M': np.array([0.5, 0.0, 0.0])} path = [["\\Gamma", "M", "K", "\\Gamma", "A", "L", "H", "A"], ["L", "M"], ["K", "H"]] return {'kpoints': kpoints, 'path': path} def rhl1(self, alpha): self.name = "RHL1" eta = (1 + 4 * cos(alpha)) / (2 + 4 * cos(alpha)) nu = 3.0 / 4.0 - eta / 2.0 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'B': np.array([eta, 0.5, 1.0 - eta]), 'B_1': np.array([1.0 / 2.0, 1.0 - eta, eta - 1.0]), 'F': np.array([0.5, 0.5, 0.0]), 'L': np.array([0.5, 0.0, 0.0]), 'L_1': np.array([0.0, 0.0, -0.5]), 'P': np.array([eta, nu, nu]), 'P_1': np.array([1.0 - nu, 1.0 - nu, 1.0 - eta]), 'P_2': np.array([nu, nu, eta - 1.0]), 'Q': np.array([1.0 - nu, nu, 0.0]), 'X': np.array([nu, 0.0, -nu]), 'Z': np.array([0.5, 0.5, 0.5])} path = [["\\Gamma", "L", "B_1"], ["B", "Z", "\\Gamma", "X"], ["Q", "F", "P_1", "Z"], ["L", "P"]] return {'kpoints': kpoints, 'path': path} def rhl2(self, alpha): self.name = "RHL2" eta = 1 / (2 * tan(alpha / 2.0) ** 2) nu = 3.0 / 4.0 - eta / 2.0 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([0.5, -0.5, 0.0]), 'L': np.array([0.5, 0.0, 0.0]), 'P': np.array([1 - nu, -nu, 1 - nu]), 'P_1': np.array([nu, nu - 1.0, nu - 1.0]), 'Q': np.array([eta, eta, eta]), 'Q_1': np.array([1.0 - eta, -eta, -eta]), 'Z': np.array([0.5, -0.5, 0.5])} path = [["\\Gamma", "P", "Z", "Q", "\\Gamma", "F", "P_1", "Q_1", "L", "Z"]] return {'kpoints': kpoints, 'path': path} def mcl(self, b, c, beta): self.name = "MCL" eta = (1 - b * cos(beta) / c) / (2 * sin(beta) ** 2) nu = 0.5 - eta * c * cos(beta) / b kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5, 0.0]), 'C': np.array([0.0, 0.5, 0.5]), 'D': np.array([0.5, 0.0, 0.5]), 'D_1': np.array([0.5, 0.5, -0.5]), 'E': np.array([0.5, 0.5, 0.5]), 'H': np.array([0.0, eta, 1.0 - nu]), 'H_1': np.array([0.0, 1.0 - eta, nu]), 'H_2': np.array([0.0, eta, -nu]), 'M': np.array([0.5, eta, 1.0 - nu]), 'M_1': np.array([0.5, 1 - eta, nu]), 'M_2': np.array([0.5, 1 - eta, nu]), 'X': np.array([0.0, 0.5, 0.0]), 'Y': np.array([0.0, 0.0, 0.5]), 'Y_1': np.array([0.0, 0.0, -0.5]), 'Z': np.array([0.5, 0.0, 0.0])} path = [["\\Gamma", "Y", "H", "C", "E", "M_1", "A", "X", "H_1"], ["M", "D", "Z"], ["Y", "D"]] return {'kpoints': kpoints, 'path': path} def mclc1(self, a, b, c, alpha): self.name = "MCLC1" zeta = (2 - b * cos(alpha) / c) / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b psi = 0.75 - a ** 2 / (4 * b ** 2 * sin(alpha) ** 2) phi = psi + (0.75 - psi) * b * cos(alpha) / c kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'F': np.array([1 - zeta, 1 - zeta, 1 - eta]), 'F_1': np.array([zeta, zeta, eta]), 'F_2': np.array([-zeta, -zeta, 1 - eta]), #'F_3': np.array([1 - zeta, -zeta, 1 - eta]), 'I': np.array([phi, 1 - phi, 0.5]), 'I_1': np.array([1 - phi, phi - 1, 0.5]), 'L': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'X': np.array([1 - psi, psi - 1, 0.0]), 'X_1': np.array([psi, 1 - psi, 0.0]), 'X_2': np.array([psi - 1, -psi, 0.0]), 'Y': np.array([0.5, 0.5, 0.0]), 'Y_1': np.array([-0.5, -0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "F_1"], ["Y", "X_1"], ["X", "\\Gamma", "N"], ["M", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def mclc2(self, a, b, c, alpha): self.name = "MCLC2" zeta = (2 - b * cos(alpha) / c) / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b psi = 0.75 - a ** 2 / (4 * b ** 2 * sin(alpha) ** 2) phi = psi + (0.75 - psi) * b * cos(alpha) / c kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'F': np.array([1 - zeta, 1 - zeta, 1 - eta]), 'F_1': np.array([zeta, zeta, eta]), 'F_2': np.array([-zeta, -zeta, 1 - eta]), 'F_3': np.array([1 - zeta, -zeta, 1 - eta]), 'I': np.array([phi, 1 - phi, 0.5]), 'I_1': np.array([1 - phi, phi - 1, 0.5]), 'L': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'X': np.array([1 - psi, psi - 1, 0.0]), 'X_1': np.array([psi, 1 - psi, 0.0]), 'X_2': np.array([psi - 1, -psi, 0.0]), 'Y': np.array([0.5, 0.5, 0.0]), 'Y_1': np.array([-0.5, -0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "F_1"], ["N", "\\Gamma", "M"]] return {'kpoints': kpoints, 'path': path} def mclc3(self, a, b, c, alpha): self.name = "MCLC3" mu = (1 + b ** 2 / a ** 2) / 4.0 delta = b * c * cos(alpha) / (2 * a ** 2) zeta = mu - 0.25 + (1 - b * cos(alpha) / c)\ / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b phi = 1 + zeta - 2 * mu psi = eta - 2 * delta kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([1 - phi, 1 - phi, 1 - psi]), 'F_1': np.array([phi, phi - 1, psi]), 'F_2': np.array([1 - phi, -phi, 1 - psi]), 'H': np.array([zeta, zeta, eta]), 'H_1': np.array([1 - zeta, -zeta, 1 - eta]), 'H_2': np.array([-zeta, -zeta, 1 - eta]), 'I': np.array([0.5, -0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'X': np.array([0.5, -0.5, 0.0]), 'Y': np.array([mu, mu, delta]), 'Y_1': np.array([1 - mu, -mu, -delta]), 'Y_2': np.array([-mu, -mu, -delta]), 'Y_3': np.array([mu, mu - 1, delta]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "Y", "F", "H", "Z", "I", "F_1"], ["H_1", "Y_1", "X", "\\Gamma", "N"], ["M", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def mclc4(self, a, b, c, alpha): self.name = "MCLC4" mu = (1 + b ** 2 / a ** 2) / 4.0 delta = b * c * cos(alpha) / (2 * a ** 2) zeta = mu - 0.25 + (1 - b * cos(alpha) / c)\ / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b phi = 1 + zeta - 2 * mu psi = eta - 2 * delta kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([1 - phi, 1 - phi, 1 - psi]), 'F_1': np.array([phi, phi - 1, psi]), 'F_2': np.array([1 - phi, -phi, 1 - psi]), 'H': np.array([zeta, zeta, eta]), 'H_1': np.array([1 - zeta, -zeta, 1 - eta]), 'H_2': np.array([-zeta, -zeta, 1 - eta]), 'I': np.array([0.5, -0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'X': np.array([0.5, -0.5, 0.0]), 'Y': np.array([mu, mu, delta]), 'Y_1': np.array([1 - mu, -mu, -delta]), 'Y_2': np.array([-mu, -mu, -delta]), 'Y_3': np.array([mu, mu - 1, delta]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "Y", "F", "H", "Z", "I"], ["H_1", "Y_1", "X", "\\Gamma", "N"], ["M", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def mclc5(self, a, b, c, alpha): self.name = "MCLC5" zeta = (b ** 2 / a ** 2 + (1 - b * cos(alpha) / c) / sin(alpha) ** 2) / 4 eta = 0.5 + 2 * zeta * c * cos(alpha) / b mu = eta / 2 + b ** 2 / (4 * a ** 2) \ - b * c * cos(alpha) / (2 * a ** 2) nu = 2 * mu - zeta rho = 1 - zeta * a ** 2 / b ** 2 omega = (4 * nu - 1 - b ** 2 * sin(alpha) ** 2 / a ** 2)\ * c / (2 * b * cos(alpha)) delta = zeta * c * cos(alpha) / b + omega / 2 - 0.25 kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([nu, nu, omega]), 'F_1': np.array([1 - nu, 1 - nu, 1 - omega]), 'F_2': np.array([nu, nu - 1, omega]), 'H': np.array([zeta, zeta, eta]), 'H_1': np.array([1 - zeta, -zeta, 1 - eta]), 'H_2': np.array([-zeta, -zeta, 1 - eta]), 'I': np.array([rho, 1 - rho, 0.5]), 'I_1': np.array([1 - rho, rho - 1, 0.5]), 'L': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'X': np.array([0.5, -0.5, 0.0]), 'Y': np.array([mu, mu, delta]), 'Y_1': np.array([1 - mu, -mu, -delta]), 'Y_2': np.array([-mu, -mu, -delta]), 'Y_3': np.array([mu, mu - 1, delta]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "H", "F_1"], ["H_1", "Y_1", "X", "\\Gamma", "N"], ["M", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def tria(self): self.name = "TRI1a" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'L': np.array([0.5, 0.5, 0.0]), 'M': np.array([0.0, 0.5, 0.5]), 'N': np.array([0.5, 0.0, 0.5]), 'R': np.array([0.5, 0.5, 0.5]), 'X': np.array([0.5, 0.0, 0.0]), 'Y': np.array([0.0, 0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["X", "\\Gamma", "Y"], ["L", "\\Gamma", "Z"], ["N", "\\Gamma", "M"], ["R", "\\Gamma"]] return {'kpoints': kpoints, 'path': path} def trib(self): self.name = "TRI1b" kpoints = {'\\Gamma': np.array([0.0, 0.0, 0.0]), 'L': np.array([0.5, -0.5, 0.0]), 'M': np.array([0.0, 0.0, 0.5]), 'N': np.array([-0.5, -0.5, 0.5]), 'R': np.array([0.0, -0.5, 0.5]), 'X': np.array([0.0, -0.5, 0.0]), 'Y': np.array([0.5, 0.0, 0.0]), 'Z': np.array([-0.5, 0.0, 0.5])} path = [["X", "\\Gamma", "Y"], ["L", "\\Gamma", "Z"], ["N", "\\Gamma", "M"], ["R", "\\Gamma"]] return {'kpoints': kpoints, 'path': path}
class HighSymmKpath(object): """ This class looks for path along high symmetry lines in the Brillouin Zone. It is based on Setyawan, W., & Curtarolo, S. (2010). High-throughput electronic band structure calculations: Challenges and tools. Computational Materials Science, 49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010 The symmetry is determined by spglib through the SpacegroupAnalyzer class Args: structure (Structure): Structure object symprec (float): Tolerance for symmetry finding angle_tolerance (float): Angle tolerance for symmetry finding. """ def __init__(self, structure, symprec=0.01, angle_tolerance=5): self._structure = structure self._sym = SpacegroupAnalyzer(structure, symprec=symprec, angle_tolerance=angle_tolerance) self._prim = self._sym\ .get_primitive_standard_structure(international_monoclinic=False) self._conv = self._sym.get_conventional_standard_structure(international_monoclinic=False) self._prim_rec = self._prim.lattice.reciprocal_lattice self._kpath = None lattice_type = self._sym.get_lattice_type() spg_symbol = self._sym.get_spacegroup_symbol() if lattice_type == "cubic": if "P" in spg_symbol: self._kpath = self.cubic() elif "F" in spg_symbol: self._kpath = self.fcc() elif "I" in spg_symbol: self._kpath = self.bcc() else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "tetragonal": if "P" in spg_symbol: self._kpath = self.tet() elif "I" in spg_symbol: a = self._conv.lattice.abc[0] c = self._conv.lattice.abc[2] if c < a: self._kpath = self.bctet1(c, a) else: self._kpath = self.bctet2(c, a) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "orthorhombic": a = self._conv.lattice.abc[0] b = self._conv.lattice.abc[1] c = self._conv.lattice.abc[2] if "P" in spg_symbol: self._kpath = self.orc() elif "F" in spg_symbol: if 1 / a ** 2 > 1 / b ** 2 + 1 / c ** 2: self._kpath = self.orcf1(a, b, c) elif 1 / a ** 2 < 1 / b ** 2 + 1 / c ** 2: self._kpath = self.orcf2(a, b, c) else: self._kpath = self.orcf3(a, b, c) elif "I" in spg_symbol: self._kpath = self.orci(a, b, c) elif "C" in spg_symbol: self._kpath = self.orcc(a, b, c) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "hexagonal": self._kpath = self.hex() elif lattice_type == "rhombohedral": alpha = self._prim.lattice.lengths_and_angles[1][0] if alpha < 90: self._kpath = self.rhl1(alpha * pi / 180) else: self._kpath = self.rhl2(alpha * pi / 180) elif lattice_type == "monoclinic": a, b, c = self._conv.lattice.abc alpha = self._conv.lattice.lengths_and_angles[1][0] #beta = self._conv.lattice.lengths_and_angles[1][1] if "P" in spg_symbol: self._kpath = self.mcl(b, c, alpha * pi / 180) elif "C" in spg_symbol: kgamma = self._prim_rec.lengths_and_angles[1][2] if kgamma > 90: self._kpath = self.mclc1(a, b, c, alpha * pi / 180) if kgamma == 90: self._kpath = self.mclc2(a, b, c, alpha * pi / 180) if kgamma < 90: if b * cos(alpha * pi / 180) / c\ + b ** 2 * sin(alpha) ** 2 / a ** 2 < 1: self._kpath = self.mclc3(a, b, c, alpha * pi / 180) if b * cos(alpha * pi / 180) / c \ + b ** 2 * sin(alpha) ** 2 / a ** 2 == 1: self._kpath = self.mclc4(a, b, c, alpha * pi / 180) if b * cos(alpha * pi / 180) / c \ + b ** 2 * sin(alpha) ** 2 / a ** 2 > 1: self._kpath = self.mclc5(a, b, c, alpha * pi / 180) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "triclinic": kalpha = self._prim_rec.lengths_and_angles[1][0] kbeta = self._prim_rec.lengths_and_angles[1][1] kgamma = self._prim_rec.lengths_and_angles[1][2] if kalpha > 90 and kbeta > 90 and kgamma > 90: self._kpath = self.tria() if kalpha < 90 and kbeta < 90 and kgamma < 90: self._kpath = self.trib() if kalpha > 90 and kbeta > 90 and kgamma == 90: self._kpath = self.tria() if kalpha < 90 and kbeta < 90 and kgamma == 90: self._kpath = self.trib() else: warn("Unknown lattice type %s" % lattice_type) @property def structure(self): """ Returns: The standardized primitive structure """ return self._prim @property def kpath(self): """ Returns: The symmetry line path in reciprocal space """ return self._kpath def get_kpoints(self, line_density=20, coords_are_cartesian=True): """ Returns: the kpoints along the paths in cartesian coordinates together with the labels for symmetry points -Wei """ list_k_points = [] sym_point_labels = [] for b in self.kpath['path']: for i in range(1, len(b)): start = np.array(self.kpath['kpoints'][b[i - 1]]) end = np.array(self.kpath['kpoints'][b[i]]) distance = np.linalg.norm( self._prim_rec.get_cartesian_coords(start) - self._prim_rec.get_cartesian_coords(end)) nb = int(ceil(distance * line_density)) sym_point_labels.extend([b[i - 1]] + [''] * (nb - 1) + [b[i]]) list_k_points.extend( [self._prim_rec.get_cartesian_coords(start) + float(i) / float(nb) * (self._prim_rec.get_cartesian_coords(end) - self._prim_rec.get_cartesian_coords(start)) for i in range(0, nb + 1)]) if coords_are_cartesian: return list_k_points, sym_point_labels else: frac_k_points = [self._prim_rec.get_fractional_coords(k) for k in list_k_points] return frac_k_points, sym_point_labels def get_kpath_plot(self, **kwargs): """ Gives the plot (as a matplotlib object) of the symmetry line path in the Brillouin Zone. Returns: `matplotlib` figure. ================ ==================================================== kwargs Meaning ================ ==================================================== title Title of the plot (Default: None). show True to show the figure (default: True). savefig 'abc.png' or 'abc.eps' to save the figure to a file. size_kwargs Dictionary with options passed to fig.set_size_inches example: size_kwargs=dict(w=3, h=4) tight_layout True if to call fig.tight_layout (default: False) ================ ==================================================== """ lines = [[self.kpath['kpoints'][k] for k in p] for p in self.kpath['path']] return plot_brillouin_zone(bz_lattice=self._prim_rec, lines=lines, labels=self.kpath['kpoints'], **kwargs) def cubic(self): self.name = "CUB" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'X': np.array([0.0, 0.5, 0.0]), 'R': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.5, 0.0])} path = [["\Gamma", "X", "M", "\Gamma", "R", "X"], ["M", "R"]] return {'kpoints': kpoints, 'path': path} def fcc(self): self.name = "FCC" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'K': np.array([3.0 / 8.0, 3.0 / 8.0, 3.0 / 4.0]), 'L': np.array([0.5, 0.5, 0.5]), 'U': np.array([5.0 / 8.0, 1.0 / 4.0, 5.0 / 8.0]), 'W': np.array([0.5, 1.0 / 4.0, 3.0 / 4.0]), 'X': np.array([0.5, 0.0, 0.5])} path = [["\Gamma", "X", "W", "K", "\Gamma", "L", "U", "W", "L", "K"], ["U", "X"]] return {'kpoints': kpoints, 'path': path} def bcc(self): self.name = "BCC" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'H': np.array([0.5, -0.5, 0.5]), 'P': np.array([0.25, 0.25, 0.25]), 'N': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "H", "N", "\Gamma", "P", "H"], ["P", "N"]] return {'kpoints': kpoints, 'path': path} def tet(self): self.name = "TET" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.5, 0.0]), 'R': np.array([0.0, 0.5, 0.5]), 'X': np.array([0.0, 0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "X", "M", "\Gamma", "Z", "R", "A", "Z"], ["X", "R"], ["M", "A"]] return {'kpoints': kpoints, 'path': path} def bctet1(self, c, a): self.name = "BCT1" eta = (1 + c ** 2 / a ** 2) / 4.0 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'M': np.array([-0.5, 0.5, 0.5]), 'N': np.array([0.0, 0.5, 0.0]), 'P': np.array([0.25, 0.25, 0.25]), 'X': np.array([0.0, 0.0, 0.5]), 'Z': np.array([eta, eta, -eta]), 'Z_1': np.array([-eta, 1 - eta, eta])} path = [["\Gamma", "X", "M", "\Gamma", "Z", "P", "N", "Z_1", "M"], ["X", "P"]] return {'kpoints': kpoints, 'path': path} def bctet2(self, c, a): self.name = "BCT2" eta = (1 + a ** 2 / c ** 2) / 4.0 zeta = a ** 2 / (2 * c ** 2) kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'N': np.array([0.0, 0.5, 0.0]), 'P': np.array([0.25, 0.25, 0.25]), '\Sigma': np.array([-eta, eta, eta]), '\Sigma_1': np.array([eta, 1 - eta, -eta]), 'X': np.array([0.0, 0.0, 0.5]), 'Y': np.array([-zeta, zeta, 0.5]), 'Y_1': np.array([0.5, 0.5, -zeta]), 'Z': np.array([0.5, 0.5, -0.5])} path = [["\Gamma", "X", "Y", "\Sigma", "\Gamma", "Z", "\Sigma_1", "N", "P", "Y_1", "Z"], ["X", "P"]] return {'kpoints': kpoints, 'path': path} def orc(self): self.name = "ORC" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'R': np.array([0.5, 0.5, 0.5]), 'S': np.array([0.5, 0.5, 0.0]), 'T': np.array([0.0, 0.5, 0.5]), 'U': np.array([0.5, 0.0, 0.5]), 'X': np.array([0.5, 0.0, 0.0]), 'Y': np.array([0.0, 0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "X", "S", "Y", "\Gamma", "Z", "U", "R", "T", "Z"], ["Y", "T"], ["U", "X"], ["S", "R"]] return {'kpoints': kpoints, 'path': path} def orcf1(self, a, b, c): self.name = "ORCF1" zeta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 eta = (1 + a ** 2 / b ** 2 + a ** 2 / c ** 2) / 4 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5 + zeta, zeta]), 'A_1': np.array([0.5, 0.5 - zeta, 1 - zeta]), 'L': np.array([0.5, 0.5, 0.5]), 'T': np.array([1, 0.5, 0.5]), 'X': np.array([0.0, eta, eta]), 'X_1': np.array([1, 1 - eta, 1 - eta]), 'Y': np.array([0.5, 0.0, 0.5]), 'Z': np.array([0.5, 0.5, 0.0])} path = [["\Gamma", "Y", "T", "Z", "\Gamma", "X", "A_1", "Y"], ["T", "X_1"], ["X", "A", "Z"], ["L", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def orcf2(self, a, b, c): self.name = "ORCF2" phi = (1 + c ** 2 / b ** 2 - c ** 2 / a ** 2) / 4 eta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 delta = (1 + b ** 2 / a ** 2 - b ** 2 / c ** 2) / 4 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'C': np.array([0.5, 0.5 - eta, 1 - eta]), 'C_1': np.array([0.5, 0.5 + eta, eta]), 'D': np.array([0.5 - delta, 0.5, 1 - delta]), 'D_1': np.array([0.5 + delta, 0.5, delta]), 'L': np.array([0.5, 0.5, 0.5]), 'H': np.array([1 - phi, 0.5 - phi, 0.5]), 'H_1': np.array([phi, 0.5 + phi, 0.5]), 'X': np.array([0.0, 0.5, 0.5]), 'Y': np.array([0.5, 0.0, 0.5]), 'Z': np.array([0.5, 0.5, 0.0])} path = [["\Gamma", "Y", "C", "D", "X", "\Gamma", "Z", "D_1", "H", "C"], ["C_1", "Z"], ["X", "H_1"], ["H", "Y"], ["L", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def orcf3(self, a, b, c): self.name = "ORCF3" zeta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 eta = (1 + a ** 2 / b ** 2 + a ** 2 / c ** 2) / 4 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5 + zeta, zeta]), 'A_1': np.array([0.5, 0.5 - zeta, 1 - zeta]), 'L': np.array([0.5, 0.5, 0.5]), 'T': np.array([1, 0.5, 0.5]), 'X': np.array([0.0, eta, eta]), 'X_1': np.array([1, 1 - eta, 1 - eta]), 'Y': np.array([0.5, 0.0, 0.5]), 'Z': np.array([0.5, 0.5, 0.0])} path = [["\Gamma", "Y", "T", "Z", "\Gamma", "X", "A_1", "Y"], ["X", "A", "Z"], ["L", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def orci(self, a, b, c): self.name = "ORCI" zeta = (1 + a ** 2 / c ** 2) / 4 eta = (1 + b ** 2 / c ** 2) / 4 delta = (b ** 2 - a ** 2) / (4 * c ** 2) mu = (a ** 2 + b ** 2) / (4 * c ** 2) kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'L': np.array([-mu, mu, 0.5 - delta]), 'L_1': np.array([mu, -mu, 0.5 + delta]), 'L_2': np.array([0.5 - delta, 0.5 + delta, -mu]), 'R': np.array([0.0, 0.5, 0.0]), 'S': np.array([0.5, 0.0, 0.0]), 'T': np.array([0.0, 0.0, 0.5]), 'W': np.array([0.25, 0.25, 0.25]), 'X': np.array([-zeta, zeta, zeta]), 'X_1': np.array([zeta, 1 - zeta, -zeta]), 'Y': np.array([eta, -eta, eta]), 'Y_1': np.array([1 - eta, eta, -eta]), 'Z': np.array([0.5, 0.5, -0.5])} path = [["\Gamma", "X", "L", "T", "W", "R", "X_1", "Z", "\Gamma", "Y", "S", "W"], ["L_1", "Y"], ["Y_1", "Z"]] return {'kpoints': kpoints, 'path': path} def orcc(self, a, b, c): self.name = "ORCC" zeta = (1 + a ** 2 / b ** 2) / 4 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([zeta, zeta, 0.5]), 'A_1': np.array([-zeta, 1 - zeta, 0.5]), 'R': np.array([0.0, 0.5, 0.5]), 'S': np.array([0.0, 0.5, 0.0]), 'T': np.array([-0.5, 0.5, 0.5]), 'X': np.array([zeta, zeta, 0.0]), 'X_1': np.array([-zeta, 1 - zeta, 0.0]), 'Y': np.array([-0.5, 0.5, 0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "X", "S", "R", "A", "Z", "\Gamma", "Y", "X_1", "A_1", "T", "Y"], ["Z", "T"]] return {'kpoints': kpoints, 'path': path} def hex(self): self.name = "HEX" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.0, 0.0, 0.5]), 'H': np.array([1.0 / 3.0, 1.0 / 3.0, 0.5]), 'K': np.array([1.0 / 3.0, 1.0 / 3.0, 0.0]), 'L': np.array([0.5, 0.0, 0.5]), 'M': np.array([0.5, 0.0, 0.0])} path = [["\Gamma", "M", "K", "\Gamma", "A", "L", "H", "A"], ["L", "M"], ["K", "H"]] return {'kpoints': kpoints, 'path': path} def rhl1(self, alpha): self.name = "RHL1" eta = (1 + 4 * cos(alpha)) / (2 + 4 * cos(alpha)) nu = 3.0 / 4.0 - eta / 2.0 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'B': np.array([eta, 0.5, 1.0 - eta]), 'B_1': np.array([1.0 / 2.0, 1.0 - eta, eta - 1.0]), 'F': np.array([0.5, 0.5, 0.0]), 'L': np.array([0.5, 0.0, 0.0]), 'L_1': np.array([0.0, 0.0, -0.5]), 'P': np.array([eta, nu, nu]), 'P_1': np.array([1.0 - nu, 1.0 - nu, 1.0 - eta]), 'P_2': np.array([nu, nu, eta - 1.0]), 'Q': np.array([1.0 - nu, nu, 0.0]), 'X': np.array([nu, 0.0, -nu]), 'Z': np.array([0.5, 0.5, 0.5])} path = [["\Gamma", "L", "B_1"], ["B", "Z", "\Gamma", "X"], ["Q", "F", "P_1", "Z"], ["L", "P"]] return {'kpoints': kpoints, 'path': path} def rhl2(self, alpha): self.name = "RHL2" eta = 1 / (2 * tan(alpha / 2.0) ** 2) nu = 3.0 / 4.0 - eta / 2.0 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([0.5, -0.5, 0.0]), 'L': np.array([0.5, 0.0, 0.0]), 'P': np.array([1 - nu, -nu, 1 - nu]), 'P_1': np.array([nu, nu - 1.0, nu - 1.0]), 'Q': np.array([eta, eta, eta]), 'Q_1': np.array([1.0 - eta, -eta, -eta]), 'Z': np.array([0.5, -0.5, 0.5])} path = [["\Gamma", "P", "Z", "Q", "\Gamma", "F", "P_1", "Q_1", "L", "Z"]] return {'kpoints': kpoints, 'path': path} def mcl(self, b, c, beta): self.name = "MCL" eta = (1 - b * cos(beta) / c) / (2 * sin(beta) ** 2) nu = 0.5 - eta * c * cos(beta) / b kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5, 0.0]), 'C': np.array([0.0, 0.5, 0.5]), 'D': np.array([0.5, 0.0, 0.5]), 'D_1': np.array([0.5, 0.5, -0.5]), 'E': np.array([0.5, 0.5, 0.5]), 'H': np.array([0.0, eta, 1.0 - nu]), 'H_1': np.array([0.0, 1.0 - eta, nu]), 'H_2': np.array([0.0, eta, -nu]), 'M': np.array([0.5, eta, 1.0 - nu]), 'M_1': np.array([0.5, 1 - eta, nu]), 'M_2': np.array([0.5, 1 - eta, nu]), 'X': np.array([0.0, 0.5, 0.0]), 'Y': np.array([0.0, 0.0, 0.5]), 'Y_1': np.array([0.0, 0.0, -0.5]), 'Z': np.array([0.5, 0.0, 0.0])} path = [["\Gamma", "Y", "H", "C", "E", "M_1", "A", "X", "H_1"], ["M", "D", "Z"], ["Y", "D"]] return {'kpoints': kpoints, 'path': path} def mclc1(self, a, b, c, alpha): self.name = "MCLC1" zeta = (2 - b * cos(alpha) / c) / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b psi = 0.75 - a ** 2 / (4 * b ** 2 * sin(alpha) ** 2) phi = psi + (0.75 - psi) * b * cos(alpha) / c kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'F': np.array([1 - zeta, 1 - zeta, 1 - eta]), 'F_1': np.array([zeta, zeta, eta]), 'F_2': np.array([-zeta, -zeta, 1 - eta]), #'F_3': np.array([1 - zeta, -zeta, 1 - eta]), 'I': np.array([phi, 1 - phi, 0.5]), 'I_1': np.array([1 - phi, phi - 1, 0.5]), 'L': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'X': np.array([1 - psi, psi - 1, 0.0]), 'X_1': np.array([psi, 1 - psi, 0.0]), 'X_2': np.array([psi - 1, -psi, 0.0]), 'Y': np.array([0.5, 0.5, 0.0]), 'Y_1': np.array([-0.5, -0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "F_1"], ["Y", "X_1"], ["X", "\Gamma", "N"], ["M", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def mclc2(self, a, b, c, alpha): self.name = "MCLC2" zeta = (2 - b * cos(alpha) / c) / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b psi = 0.75 - a ** 2 / (4 * b ** 2 * sin(alpha) ** 2) phi = psi + (0.75 - psi) * b * cos(alpha) / c kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'F': np.array([1 - zeta, 1 - zeta, 1 - eta]), 'F_1': np.array([zeta, zeta, eta]), 'F_2': np.array([-zeta, -zeta, 1 - eta]), 'F_3': np.array([1 - zeta, -zeta, 1 - eta]), 'I': np.array([phi, 1 - phi, 0.5]), 'I_1': np.array([1 - phi, phi - 1, 0.5]), 'L': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'X': np.array([1 - psi, psi - 1, 0.0]), 'X_1': np.array([psi, 1 - psi, 0.0]), 'X_2': np.array([psi - 1, -psi, 0.0]), 'Y': np.array([0.5, 0.5, 0.0]), 'Y_1': np.array([-0.5, -0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "F_1"], ["N", "\Gamma", "M"]] return {'kpoints': kpoints, 'path': path} def mclc3(self, a, b, c, alpha): self.name = "MCLC3" mu = (1 + b ** 2 / a ** 2) / 4.0 delta = b * c * cos(alpha) / (2 * a ** 2) zeta = mu - 0.25 + (1 - b * cos(alpha) / c)\ / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b phi = 1 + zeta - 2 * mu psi = eta - 2 * delta kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([1 - phi, 1 - phi, 1 - psi]), 'F_1': np.array([phi, phi - 1, psi]), 'F_2': np.array([1 - phi, -phi, 1 - psi]), 'H': np.array([zeta, zeta, eta]), 'H_1': np.array([1 - zeta, -zeta, 1 - eta]), 'H_2': np.array([-zeta, -zeta, 1 - eta]), 'I': np.array([0.5, -0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'X': np.array([0.5, -0.5, 0.0]), 'Y': np.array([mu, mu, delta]), 'Y_1': np.array([1 - mu, -mu, -delta]), 'Y_2': np.array([-mu, -mu, -delta]), 'Y_3': np.array([mu, mu - 1, delta]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "Y", "F", "H", "Z", "I", "F_1"], ["H_1", "Y_1", "X", "\Gamma", "N"], ["M", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def mclc4(self, a, b, c, alpha): self.name = "MCLC4" mu = (1 + b ** 2 / a ** 2) / 4.0 delta = b * c * cos(alpha) / (2 * a ** 2) zeta = mu - 0.25 + (1 - b * cos(alpha) / c)\ / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b phi = 1 + zeta - 2 * mu psi = eta - 2 * delta kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([1 - phi, 1 - phi, 1 - psi]), 'F_1': np.array([phi, phi - 1, psi]), 'F_2': np.array([1 - phi, -phi, 1 - psi]), 'H': np.array([zeta, zeta, eta]), 'H_1': np.array([1 - zeta, -zeta, 1 - eta]), 'H_2': np.array([-zeta, -zeta, 1 - eta]), 'I': np.array([0.5, -0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'X': np.array([0.5, -0.5, 0.0]), 'Y': np.array([mu, mu, delta]), 'Y_1': np.array([1 - mu, -mu, -delta]), 'Y_2': np.array([-mu, -mu, -delta]), 'Y_3': np.array([mu, mu - 1, delta]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "Y", "F", "H", "Z", "I"], ["H_1", "Y_1", "X", "\Gamma", "N"], ["M", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def mclc5(self, a, b, c, alpha): self.name = "MCLC5" zeta = (b ** 2 / a ** 2 + (1 - b * cos(alpha) / c) / sin(alpha) ** 2) / 4 eta = 0.5 + 2 * zeta * c * cos(alpha) / b mu = eta / 2 + b ** 2 / (4 * a ** 2) \ - b * c * cos(alpha) / (2 * a ** 2) nu = 2 * mu - zeta rho = 1 - zeta * a ** 2 / b ** 2 omega = (4 * nu - 1 - b ** 2 * sin(alpha) ** 2 / a ** 2)\ * c / (2 * b * cos(alpha)) delta = zeta * c * cos(alpha) / b + omega / 2 - 0.25 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([nu, nu, omega]), 'F_1': np.array([1 - nu, 1 - nu, 1 - omega]), 'F_2': np.array([nu, nu - 1, omega]), 'H': np.array([zeta, zeta, eta]), 'H_1': np.array([1 - zeta, -zeta, 1 - eta]), 'H_2': np.array([-zeta, -zeta, 1 - eta]), 'I': np.array([rho, 1 - rho, 0.5]), 'I_1': np.array([1 - rho, rho - 1, 0.5]), 'L': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'X': np.array([0.5, -0.5, 0.0]), 'Y': np.array([mu, mu, delta]), 'Y_1': np.array([1 - mu, -mu, -delta]), 'Y_2': np.array([-mu, -mu, -delta]), 'Y_3': np.array([mu, mu - 1, delta]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "H", "F_1"], ["H_1", "Y_1", "X", "\Gamma", "N"], ["M", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def tria(self): self.name = "TRI1a" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'L': np.array([0.5, 0.5, 0.0]), 'M': np.array([0.0, 0.5, 0.5]), 'N': np.array([0.5, 0.0, 0.5]), 'R': np.array([0.5, 0.5, 0.5]), 'X': np.array([0.5, 0.0, 0.0]), 'Y': np.array([0.0, 0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["X", "\Gamma", "Y"], ["L", "\Gamma", "Z"], ["N", "\Gamma", "M"], ["R", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def trib(self): self.name = "TRI1b" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'L': np.array([0.5, -0.5, 0.0]), 'M': np.array([0.0, 0.0, 0.5]), 'N': np.array([-0.5, -0.5, 0.5]), 'R': np.array([0.0, -0.5, 0.5]), 'X': np.array([0.0, -0.5, 0.0]), 'Y': np.array([0.5, 0.0, 0.0]), 'Z': np.array([-0.5, 0.0, 0.5])} path = [["X", "\Gamma", "Y"], ["L", "\Gamma", "Z"], ["N", "\Gamma", "M"], ["R", "\Gamma"]] return {'kpoints': kpoints, 'path': path}
def __init__( self, api_key, list_of_elements=[], indices_dict=None, slab_size=10, vac_size=10, host=None, port=None, user=None, password=None, symprec=0.001, angle_tolerance=5, database=None, collection="Surface_Collection", fail_safe=True, reset=False, ): """ Args: api_key (str): A String API key for accessing the MaterialsProject list_of_elements ([str, ...]): A list of compounds or elements to create slabs from. Must be a string that can be searched for with MPRester. Either list_of_elements or indices_dict has to be entered in. indices_dict ({element(str): [[h,k,l], ...]}): A dictionary of miller indices corresponding to the composition formula (key) to transform into a list of slabs. Either list_of_elements or indices_dict has to be entered in. host (str): For database insertion port (int): For database insertion user (str): For database insertion password (str): For database insertion symprec (float): See SpaceGroupAnalyzer in analyzer.py angle_tolerance (int): See SpaceGroupAnalyzer in analyzer.py database (str): For database insertion """ unit_cells_dict = {} vaspdbinsert_params = { "host": host, "port": port, "user": user, "password": password, "database": database, "collection": collection, } elements = [key for key in indices_dict.keys()] if indices_dict else list_of_elements # For loop will eneumerate through all the compositional # formulas in list_of_elements or indices_dict to get a # list of relaxed conventional unit cells froom MP. These # will be used to generate all oriented unit cells and slabs. for el in elements: """ element: str, element name of Metal miller_index: hkl, e.g. [1, 1, 0] api_key: to get access to MP DB """ # This initializes the REST adaptor. Put your own API key in. mprest = MPRester(api_key) # Returns a list of MPIDs with the compositional formular, the # first MPID IS NOT the lowest energy per atom entries = mprest.get_entries(el, inc_structure="final") e_per_atom = [entry.energy_per_atom for entry in entries] for entry in entries: if min(e_per_atom) == entry.energy_per_atom: prim_unit_cell = entry.structure spa = SpacegroupAnalyzer(prim_unit_cell, symprec=symprec, angle_tolerance=angle_tolerance) conv_unit_cell = spa.get_conventional_standard_structure() print conv_unit_cell unit_cells_dict[el] = [conv_unit_cell, min(e_per_atom)] print el self.api_key = api_key self.vaspdbinsert_params = vaspdbinsert_params self.symprec = symprec self.angle_tolerance = angle_tolerance self.unit_cells_dict = unit_cells_dict self.indices_dict = indices_dict self.elements = elements self.ssize = slab_size self.vsize = vac_size self.reset = reset self.fail_safe = fail_safe
def write_etree(self, celltype, cartesian=False, bandstr=False, symprec=0.4, angle_tolerance=5): root=ET.Element('input') root.set('{http://www.w3.org/2001/XMLSchema-instance}noNamespaceSchemaLocation', 'http://xml.exciting-code.org/excitinginput.xsd') title=ET.SubElement(root,'title') title.text=self.title if cartesian: structure=ET.SubElement(root,'structure',cartesian="true",speciespath="./") else: structure=ET.SubElement(root,'structure',speciespath="./") crystal=ET.SubElement(structure,'crystal') # set scale such that lattice vector can be given in Angstrom ang2bohr=const.value('Angstrom star')/const.value('Bohr radius') crystal.set('scale',str(ang2bohr)) # determine which structure to use finder=SpacegroupAnalyzer(self.structure,symprec=symprec, angle_tolerance=angle_tolerance) if celltype=='primitive': new_struct=finder.get_primitive_standard_structure(international_monoclinic=False) elif celltype=='conventional': new_struct=finder.get_conventional_standard_structure(international_monoclinic=False) elif celltype=='unchanged': new_struct=self.structure else: raise ValueError('Type of unit cell not recognized!') # write lattice basis=new_struct.lattice.matrix for i in range(3): basevect=ET.SubElement(crystal,'basevect') basevect.text= "%16.8f %16.8f %16.8f" % (basis[i][0], basis[i][1], basis[i][2]) # write atomic positions for each species index=0 for i in new_struct.types_of_specie: species=ET.SubElement(structure,'species',speciesfile=i.symbol+ '.xml') sites=new_struct.indices_from_symbol(i.symbol) for j in sites: coord="%16.8f %16.8f %16.8f" % (new_struct[j].frac_coords[0], new_struct[j].frac_coords[1], new_struct[j].frac_coords[2]) # obtain cartesian coords from fractional ones if needed if cartesian: coord2=[] for k in range(3): inter=(new_struct[j].frac_coords[k]*basis[0][k]+\ new_struct[j].frac_coords[k]*basis[1][k]+\ new_struct[j].frac_coords[k]*basis[2][k])*ang2bohr coord2.append(inter) coord="%16.8f %16.8f %16.8f" % (coord2[0], coord2[1], coord2[2]) # write atomic positions index=index+1 atom=ET.SubElement(species,'atom',coord=coord) # write bandstructure if needed if bandstr and celltype=='primitive': kpath=HighSymmKpath(new_struct, symprec=symprec, angle_tolerance=angle_tolerance) prop=ET.SubElement(root,'properties') bandstrct=ET.SubElement(prop,'bandstructure') for i in range(len(kpath.kpath['path'])): plot=ET.SubElement(bandstrct,'plot1d') path=ET.SubElement(plot, 'path',steps='100') for j in range(len(kpath.kpath['path'][i])): symbol=kpath.kpath['path'][i][j] coords=kpath.kpath['kpoints'][symbol] coord="%16.8f %16.8f %16.8f" % (coords[0], coords[1], coords[2]) if symbol=='\\Gamma': symbol='GAMMA' pt=ET.SubElement(path,'point',coord=coord,label=symbol) elif bandstr and celltype is not 'primitive': raise ValueError("Bandstructure is only implemented for the \ standard primitive unit cell!") return root
def test_get_conventional_standard_structure(self): parser = CifParser(os.path.join(test_dir, 'bcc_1927.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 9.1980270633769461) self.assertAlmostEqual(conv.lattice.b, 9.1980270633769461) self.assertAlmostEqual(conv.lattice.c, 9.1980270633769461) parser = CifParser(os.path.join(test_dir, 'btet_1915.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 5.0615106678044235) self.assertAlmostEqual(conv.lattice.b, 5.0615106678044235) self.assertAlmostEqual(conv.lattice.c, 4.2327080177761687) parser = CifParser(os.path.join(test_dir, 'orci_1010.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 2.9542233922299999) self.assertAlmostEqual(conv.lattice.b, 4.6330325651443296) self.assertAlmostEqual(conv.lattice.c, 5.373703587040775) parser = CifParser(os.path.join(test_dir, 'orcc_1003.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 4.1430033493799998) self.assertAlmostEqual(conv.lattice.b, 31.437979757624728) self.assertAlmostEqual(conv.lattice.c, 3.99648651) parser = CifParser(os.path.join(test_dir, 'monoc_1028.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 117.53832420192903) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 14.033435583000625) self.assertAlmostEqual(conv.lattice.b, 3.96052850731) self.assertAlmostEqual(conv.lattice.c, 6.8743926325200002) parser = CifParser(os.path.join(test_dir, 'rhomb_1170.cif')) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 120) self.assertAlmostEqual(conv.lattice.a, 3.699919902005897) self.assertAlmostEqual(conv.lattice.b, 3.699919902005897) self.assertAlmostEqual(conv.lattice.c, 6.9779585500000003)
class HighSymmKpath(object): """ This class looks for path along high symmetry lines in the Brillouin Zone. It is based on Setyawan, W., & Curtarolo, S. (2010). High-throughput electronic band structure calculations: Challenges and tools. Computational Materials Science, 49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010 The symmetry is determined by spglib through the SpacegroupAnalyzer class Args: structure (Structure): Structure object symprec (float): Tolerance for symmetry finding angle_tolerance (float): Angle tolerance for symmetry finding. """ def __init__(self, structure, symprec=0.01, angle_tolerance=5): self._structure = structure self._sym = SpacegroupAnalyzer(structure, symprec=symprec, angle_tolerance=angle_tolerance) self._prim = self._sym\ .get_primitive_standard_structure(international_monoclinic=False) self._conv = self._sym.get_conventional_standard_structure(international_monoclinic=False) self._prim_rec = self._prim.lattice.reciprocal_lattice self._kpath = None lattice_type = self._sym.get_lattice_type() spg_symbol = self._sym.get_spacegroup_symbol() if lattice_type == "cubic": if "P" in spg_symbol: self._kpath = self.cubic() elif "F" in spg_symbol: self._kpath = self.fcc() elif "I" in spg_symbol: self._kpath = self.bcc() else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "tetragonal": if "P" in spg_symbol: self._kpath = self.tet() elif "I" in spg_symbol: a = self._conv.lattice.abc[0] c = self._conv.lattice.abc[2] if c < a: self._kpath = self.bctet1(c, a) else: self._kpath = self.bctet2(c, a) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "orthorhombic": a = self._conv.lattice.abc[0] b = self._conv.lattice.abc[1] c = self._conv.lattice.abc[2] if "P" in spg_symbol: self._kpath = self.orc() elif "F" in spg_symbol: if 1 / a ** 2 > 1 / b ** 2 + 1 / c ** 2: self._kpath = self.orcf1(a, b, c) elif 1 / a ** 2 < 1 / b ** 2 + 1 / c ** 2: self._kpath = self.orcf2(a, b, c) else: self._kpath = self.orcf3(a, b, c) elif "I" in spg_symbol: self._kpath = self.orci(a, b, c) elif "C" in spg_symbol: self._kpath = self.orcc(a, b, c) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "hexagonal": self._kpath = self.hex() elif lattice_type == "rhombohedral": alpha = self._prim.lattice.lengths_and_angles[1][0] if alpha < 90: self._kpath = self.rhl1(alpha * pi / 180) else: self._kpath = self.rhl2(alpha * pi / 180) elif lattice_type == "monoclinic": a, b, c = self._conv.lattice.abc alpha = self._conv.lattice.lengths_and_angles[1][0] #beta = self._conv.lattice.lengths_and_angles[1][1] if "P" in spg_symbol: self._kpath = self.mcl(b, c, alpha * pi / 180) elif "C" in spg_symbol: kgamma = self._prim_rec.lengths_and_angles[1][2] if kgamma > 90: self._kpath = self.mclc1(a, b, c, alpha * pi / 180) if kgamma == 90: self._kpath = self.mclc2(a, b, c, alpha * pi / 180) if kgamma < 90: if b * cos(alpha * pi / 180) / c\ + b ** 2 * sin(alpha) ** 2 / a ** 2 < 1: self._kpath = self.mclc3(a, b, c, alpha * pi / 180) if b * cos(alpha * pi / 180) / c \ + b ** 2 * sin(alpha) ** 2 / a ** 2 == 1: self._kpath = self.mclc4(a, b, c, alpha * pi / 180) if b * cos(alpha * pi / 180) / c \ + b ** 2 * sin(alpha) ** 2 / a ** 2 > 1: self._kpath = self.mclc5(a, b, c, alpha * pi / 180) else: warn("Unexpected value for spg_symbol: %s" % spg_symbol) elif lattice_type == "triclinic": kalpha = self._prim_rec.lengths_and_angles[1][0] kbeta = self._prim_rec.lengths_and_angles[1][1] kgamma = self._prim_rec.lengths_and_angles[1][2] if kalpha > 90 and kbeta > 90 and kgamma > 90: self._kpath = self.tria() if kalpha < 90 and kbeta < 90 and kgamma < 90: self._kpath = self.trib() if kalpha > 90 and kbeta > 90 and kgamma == 90: self._kpath = self.tria() if kalpha < 90 and kbeta < 90 and kgamma == 90: self._kpath = self.trib() else: warn("Unknown lattice type %s" % lattice_type) @property def structure(self): """ Returns: The standardized primitive structure """ return self._prim @property def kpath(self): """ Returns: The symmetry line path in reciprocal space """ return self._kpath def get_kpoints(self, line_density=20): """ Returns: the kpoints along the paths in cartesian coordinates together with the labels for symmetry points -Wei """ list_k_points = [] sym_point_labels = [] for b in self.kpath['path']: for i in range(1, len(b)): start = np.array(self.kpath['kpoints'][b[i - 1]]) end = np.array(self.kpath['kpoints'][b[i]]) distance = np.linalg.norm( self._prim_rec.get_cartesian_coords(start) - self._prim_rec.get_cartesian_coords(end)) nb = int(ceil(distance * line_density)) sym_point_labels.extend([b[i - 1]] + [''] * (nb - 1) + [b[i]]) list_k_points.extend( [self._prim_rec.get_cartesian_coords(start) + float(i) / float(nb) * (self._prim_rec.get_cartesian_coords(end) - self._prim_rec.get_cartesian_coords(start)) for i in range(0, nb + 1)]) return list_k_points, sym_point_labels def get_kpath_plot(self, **kwargs): """ Gives the plot (as a matplotlib object) of the symmetry line path in the Brillouin Zone. Returns: `matplotlib` figure. ================ ============================================================== kwargs Meaning ================ ============================================================== show True to show the figure (Default). savefig 'abc.png' or 'abc.eps'* to save the figure to a file. ================ ============================================================== """ import itertools import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import axes3d def _plot_shape_skeleton(bz, style): for iface in range(len(bz)): for line in itertools.combinations(bz[iface], 2): for jface in range(len(bz)): if iface < jface and line[0] in bz[jface]\ and line[1] in bz[jface]: ax.plot([line[0][0], line[1][0]], [line[0][1], line[1][1]], [line[0][2], line[1][2]], style) def _plot_lattice(lattice): vertex1 = lattice.get_cartesian_coords([0.0, 0.0, 0.0]) vertex2 = lattice.get_cartesian_coords([1.0, 0.0, 0.0]) ax.plot([vertex1[0], vertex2[0]], [vertex1[1], vertex2[1]], [vertex1[2], vertex2[2]], color='g', linewidth=3) vertex2 = lattice.get_cartesian_coords([0.0, 1.0, 0.0]) ax.plot([vertex1[0], vertex2[0]], [vertex1[1], vertex2[1]], [vertex1[2], vertex2[2]], color='g', linewidth=3) vertex2 = lattice.get_cartesian_coords([0.0, 0.0, 1.0]) ax.plot([vertex1[0], vertex2[0]], [vertex1[1], vertex2[1]], [vertex1[2], vertex2[2]], color='g', linewidth=3) def _plot_kpath(kpath, lattice): for line in kpath['path']: for k in range(len(line) - 1): vertex1 = lattice.get_cartesian_coords(kpath['kpoints'] [line[k]]) vertex2 = lattice.get_cartesian_coords(kpath['kpoints'] [line[k + 1]]) ax.plot([vertex1[0], vertex2[0]], [vertex1[1], vertex2[1]], [vertex1[2], vertex2[2]], color='r', linewidth=3) def _plot_labels(kpath, lattice): for k in kpath['kpoints']: label = k if k.startswith("\\") or k.find("_") != -1: label = "$" + k + "$" off = 0.01 ax.text(lattice.get_cartesian_coords(kpath['kpoints'][k])[0] + off, lattice.get_cartesian_coords(kpath['kpoints'][k])[1] + off, lattice.get_cartesian_coords(kpath['kpoints'][k])[2] + off, label, color='b', size='25') ax.scatter([lattice.get_cartesian_coords( kpath['kpoints'][k])[0]], [lattice.get_cartesian_coords( kpath['kpoints'][k])[1]], [lattice.get_cartesian_coords( kpath['kpoints'][k])[2]], color='b') fig = plt.figure() ax = axes3d.Axes3D(fig) _plot_lattice(self._prim_rec) _plot_shape_skeleton(self._prim_rec.get_wigner_seitz_cell(), '-k') _plot_kpath(self.kpath, self._prim_rec) _plot_labels(self.kpath, self._prim_rec) ax.axis("off") show = kwargs.pop("show", True) if show: plt.show() savefig = kwargs.pop("savefig", None) if savefig: fig.savefig(savefig) return fig def cubic(self): self.name = "CUB" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'X': np.array([0.0, 0.5, 0.0]), 'R': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.5, 0.0])} path = [["\Gamma", "X", "M", "\Gamma", "R", "X"], ["M", "R"]] return {'kpoints': kpoints, 'path': path} def fcc(self): self.name = "FCC" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'K': np.array([3.0 / 8.0, 3.0 / 8.0, 3.0 / 4.0]), 'L': np.array([0.5, 0.5, 0.5]), 'U': np.array([5.0 / 8.0, 1.0 / 4.0, 5.0 / 8.0]), 'W': np.array([0.5, 1.0 / 4.0, 3.0 / 4.0]), 'X': np.array([0.5, 0.0, 0.5])} path = [["\Gamma", "X", "W", "K", "\Gamma", "L", "U", "W", "L", "K"], ["U", "X"]] return {'kpoints': kpoints, 'path': path} def bcc(self): self.name = "BCC" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'H': np.array([0.5, -0.5, 0.5]), 'P': np.array([0.25, 0.25, 0.25]), 'N': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "H", "N", "\Gamma", "P", "H"], ["P", "N"]] return {'kpoints': kpoints, 'path': path} def tet(self): self.name = "TET" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.5, 0.0]), 'R': np.array([0.0, 0.5, 0.5]), 'X': np.array([0.0, 0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "X", "M", "\Gamma", "Z", "R", "A", "Z"], ["X", "R"], ["M", "A"]] return {'kpoints': kpoints, 'path': path} def bctet1(self, c, a): self.name = "BCT1" eta = (1 + c ** 2 / a ** 2) / 4.0 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'M': np.array([-0.5, 0.5, 0.5]), 'N': np.array([0.0, 0.5, 0.0]), 'P': np.array([0.25, 0.25, 0.25]), 'X': np.array([0.0, 0.0, 0.5]), 'Z': np.array([eta, eta, -eta]), 'Z_1': np.array([-eta, 1 - eta, eta])} path = [["\Gamma", "X", "M", "\Gamma", "Z", "P", "N", "Z_1", "M"], ["X", "P"]] return {'kpoints': kpoints, 'path': path} def bctet2(self, c, a): self.name = "BCT2" eta = (1 + a ** 2 / c ** 2) / 4.0 zeta = a ** 2 / (2 * c ** 2) kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'N': np.array([0.0, 0.5, 0.0]), 'P': np.array([0.25, 0.25, 0.25]), '\Sigma': np.array([-eta, eta, eta]), '\Sigma_1': np.array([eta, 1 - eta, -eta]), 'X': np.array([0.0, 0.0, 0.5]), 'Y': np.array([-zeta, zeta, 0.5]), 'Y_1': np.array([0.5, 0.5, -zeta]), 'Z': np.array([0.5, 0.5, -0.5])} path = [["\Gamma", "X", "Y", "\Sigma", "\Gamma", "Z", "\Sigma_1", "N", "P", "Y_1", "Z"], ["X", "P"]] return {'kpoints': kpoints, 'path': path} def orc(self): self.name = "ORC" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'R': np.array([0.5, 0.5, 0.5]), 'S': np.array([0.5, 0.5, 0.0]), 'T': np.array([0.0, 0.5, 0.5]), 'U': np.array([0.5, 0.0, 0.5]), 'X': np.array([0.5, 0.0, 0.0]), 'Y': np.array([0.0, 0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "X", "S", "Y", "\Gamma", "Z", "U", "R", "T", "Z"], ["Y", "T"], ["U", "X"], ["S", "R"]] return {'kpoints': kpoints, 'path': path} def orcf1(self, a, b, c): self.name = "ORCF1" zeta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 eta = (1 + a ** 2 / b ** 2 + a ** 2 / c ** 2) / 4 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5 + zeta, zeta]), 'A_1': np.array([0.5, 0.5 - zeta, 1 - zeta]), 'L': np.array([0.5, 0.5, 0.5]), 'T': np.array([1, 0.5, 0.5]), 'X': np.array([0.0, eta, eta]), 'X_1': np.array([1, 1 - eta, 1 - eta]), 'Y': np.array([0.5, 0.0, 0.5]), 'Z': np.array([0.5, 0.5, 0.0])} path = [["\Gamma", "Y", "T", "Z", "\Gamma", "X", "A_1", "Y"], ["T", "X_1"], ["X", "A", "Z"], ["L", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def orcf2(self, a, b, c): self.name = "ORCF2" phi = (1 + c ** 2 / b ** 2 - c ** 2 / a ** 2) / 4 eta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 delta = (1 + b ** 2 / a ** 2 - b ** 2 / c ** 2) / 4 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'C': np.array([0.5, 0.5 - eta, 1 - eta]), 'C_1': np.array([0.5, 0.5 + eta, eta]), 'D': np.array([0.5 - delta, 0.5, 1 - delta]), 'D_1': np.array([0.5 + delta, 0.5, delta]), 'L': np.array([0.5, 0.5, 0.5]), 'H': np.array([1 - phi, 0.5 - phi, 0.5]), 'H_1': np.array([phi, 0.5 + phi, 0.5]), 'X': np.array([0.0, 0.5, 0.5]), 'Y': np.array([0.5, 0.0, 0.5]), 'Z': np.array([0.5, 0.5, 0.0])} path = [["\Gamma", "Y", "C", "D", "X", "\Gamma", "Z", "D_1", "H", "C"], ["C_1", "Z"], ["X", "H_1"], ["H", "Y"], ["L", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def orcf3(self, a, b, c): self.name = "ORCF3" zeta = (1 + a ** 2 / b ** 2 - a ** 2 / c ** 2) / 4 eta = (1 + a ** 2 / b ** 2 + a ** 2 / c ** 2) / 4 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5 + zeta, zeta]), 'A_1': np.array([0.5, 0.5 - zeta, 1 - zeta]), 'L': np.array([0.5, 0.5, 0.5]), 'T': np.array([1, 0.5, 0.5]), 'X': np.array([0.0, eta, eta]), 'X_1': np.array([1, 1 - eta, 1 - eta]), 'Y': np.array([0.5, 0.0, 0.5]), 'Z': np.array([0.5, 0.5, 0.0])} path = [["\Gamma", "Y", "T", "Z", "\Gamma", "X", "A_1", "Y"], ["X", "A", "Z"], ["L", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def orci(self, a, b, c): self.name = "ORCI" zeta = (1 + a ** 2 / c ** 2) / 4 eta = (1 + b ** 2 / c ** 2) / 4 delta = (b ** 2 - a ** 2) / (4 * c ** 2) mu = (a ** 2 + b ** 2) / (4 * c ** 2) kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'L': np.array([-mu, mu, 0.5 - delta]), 'L_1': np.array([mu, -mu, 0.5 + delta]), 'L_2': np.array([0.5 - delta, 0.5 + delta, -mu]), 'R': np.array([0.0, 0.5, 0.0]), 'S': np.array([0.5, 0.0, 0.0]), 'T': np.array([0.0, 0.0, 0.5]), 'W': np.array([0.25, 0.25, 0.25]), 'X': np.array([-zeta, zeta, zeta]), 'X_1': np.array([zeta, 1 - zeta, -zeta]), 'Y': np.array([eta, -eta, eta]), 'Y_1': np.array([1 - eta, eta, -eta]), 'Z': np.array([0.5, 0.5, -0.5])} path = [["\Gamma", "X", "L", "T", "W", "R", "X_1", "Z", "\Gamma", "Y", "S", "W"], ["L_1", "Y"], ["Y_1", "Z"]] return {'kpoints': kpoints, 'path': path} def orcc(self, a, b, c): self.name = "ORCC" zeta = (1 + a ** 2 / b ** 2) / 4 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([zeta, zeta, 0.5]), 'A_1': np.array([-zeta, 1 - zeta, 0.5]), 'R': np.array([0.0, 0.5, 0.5]), 'S': np.array([0.0, 0.5, 0.0]), 'T': np.array([-0.5, 0.5, 0.5]), 'X': np.array([zeta, zeta, 0.0]), 'X_1': np.array([-zeta, 1 - zeta, 0.0]), 'Y': np.array([-0.5, 0.5, 0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "X", "S", "R", "A", "Z", "\Gamma", "Y", "X_1", "A_1", "T", "Y"], ["Z", "T"]] return {'kpoints': kpoints, 'path': path} def hex(self): self.name = "HEX" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.0, 0.0, 0.5]), 'H': np.array([1.0 / 3.0, 1.0 / 3.0, 0.5]), 'K': np.array([1.0 / 3.0, 1.0 / 3.0, 0.0]), 'L': np.array([0.5, 0.0, 0.5]), 'M': np.array([0.5, 0.0, 0.0])} path = [["\Gamma", "M", "K", "\Gamma", "A", "L", "H", "A"], ["L", "M"], ["K", "H"]] return {'kpoints': kpoints, 'path': path} def rhl1(self, alpha): self.name = "RHL1" eta = (1 + 4 * cos(alpha)) / (2 + 4 * cos(alpha)) nu = 3.0 / 4.0 - eta / 2.0 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'B': np.array([eta, 0.5, 1.0 - eta]), 'B_1': np.array([1.0 / 2.0, 1.0 - eta, eta - 1.0]), 'F': np.array([0.5, 0.5, 0.0]), 'L': np.array([0.5, 0.0, 0.0]), 'L_1': np.array([0.0, 0.0, -0.5]), 'P': np.array([eta, nu, nu]), 'P_1': np.array([1.0 - nu, 1.0 - nu, 1.0 - eta]), 'P_2': np.array([nu, nu, eta - 1.0]), 'Q': np.array([1.0 - nu, nu, 0.0]), 'X': np.array([nu, 0.0, -nu]), 'Z': np.array([0.5, 0.5, 0.5])} path = [["\Gamma", "L", "B_1"], ["B", "Z", "\Gamma", "X"], ["Q", "F", "P_1", "Z"], ["L", "P"]] return {'kpoints': kpoints, 'path': path} def rhl2(self, alpha): self.name = "RHL2" eta = 1 / (2 * tan(alpha / 2.0) ** 2) nu = 3.0 / 4.0 - eta / 2.0 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([0.5, -0.5, 0.0]), 'L': np.array([0.5, 0.0, 0.0]), 'P': np.array([1 - nu, -nu, 1 - nu]), 'P_1': np.array([nu, nu - 1.0, nu - 1.0]), 'Q': np.array([eta, eta, eta]), 'Q_1': np.array([1.0 - eta, -eta, -eta]), 'Z': np.array([0.5, -0.5, 0.5])} path = [["\Gamma", "P", "Z", "Q", "\Gamma", "F", "P_1", "Q_1", "L", "Z"]] return {'kpoints': kpoints, 'path': path} def mcl(self, b, c, beta): self.name = "MCL" eta = (1 - b * cos(beta) / c) / (2 * sin(beta) ** 2) nu = 0.5 - eta * c * cos(beta) / b kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'A': np.array([0.5, 0.5, 0.0]), 'C': np.array([0.0, 0.5, 0.5]), 'D': np.array([0.5, 0.0, 0.5]), 'D_1': np.array([0.5, 0.5, -0.5]), 'E': np.array([0.5, 0.5, 0.5]), 'H': np.array([0.0, eta, 1.0 - nu]), 'H_1': np.array([0.0, 1.0 - eta, nu]), 'H_2': np.array([0.0, eta, -nu]), 'M': np.array([0.5, eta, 1.0 - nu]), 'M_1': np.array([0.5, 1 - eta, nu]), 'M_2': np.array([0.5, 1 - eta, nu]), 'X': np.array([0.0, 0.5, 0.0]), 'Y': np.array([0.0, 0.0, 0.5]), 'Y_1': np.array([0.0, 0.0, -0.5]), 'Z': np.array([0.5, 0.0, 0.0])} path = [["\Gamma", "Y", "H", "C", "E", "M_1", "A", "X", "H_1"], ["M", "D", "Z"], ["Y", "D"]] return {'kpoints': kpoints, 'path': path} def mclc1(self, a, b, c, alpha): self.name = "MCLC1" zeta = (2 - b * cos(alpha) / c) / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b psi = 0.75 - a ** 2 / (4 * b ** 2 * sin(alpha) ** 2) phi = psi + (0.75 - psi) * b * cos(alpha) / c kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'F': np.array([1 - zeta, 1 - zeta, 1 - eta]), 'F_1': np.array([zeta, zeta, eta]), 'F_2': np.array([-zeta, -zeta, 1 - eta]), #'F_3': np.array([1 - zeta, -zeta, 1 - eta]), 'I': np.array([phi, 1 - phi, 0.5]), 'I_1': np.array([1 - phi, phi - 1, 0.5]), 'L': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'X': np.array([1 - psi, psi - 1, 0.0]), 'X_1': np.array([psi, 1 - psi, 0.0]), 'X_2': np.array([psi - 1, -psi, 0.0]), 'Y': np.array([0.5, 0.5, 0.0]), 'Y_1': np.array([-0.5, -0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "F_1"], ["Y", "X_1"], ["X", "\Gamma", "N"], ["M", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def mclc2(self, a, b, c, alpha): self.name = "MCLC2" zeta = (2 - b * cos(alpha) / c) / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b psi = 0.75 - a ** 2 / (4 * b ** 2 * sin(alpha) ** 2) phi = psi + (0.75 - psi) * b * cos(alpha) / c kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'F': np.array([1 - zeta, 1 - zeta, 1 - eta]), 'F_1': np.array([zeta, zeta, eta]), 'F_2': np.array([-zeta, -zeta, 1 - eta]), 'F_3': np.array([1 - zeta, -zeta, 1 - eta]), 'I': np.array([phi, 1 - phi, 0.5]), 'I_1': np.array([1 - phi, phi - 1, 0.5]), 'L': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'X': np.array([1 - psi, psi - 1, 0.0]), 'X_1': np.array([psi, 1 - psi, 0.0]), 'X_2': np.array([psi - 1, -psi, 0.0]), 'Y': np.array([0.5, 0.5, 0.0]), 'Y_1': np.array([-0.5, -0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "F_1"], ["N", "\Gamma", "M"]] return {'kpoints': kpoints, 'path': path} def mclc3(self, a, b, c, alpha): self.name = "MCLC3" mu = (1 + b ** 2 / a ** 2) / 4.0 delta = b * c * cos(alpha) / (2 * a ** 2) zeta = mu - 0.25 + (1 - b * cos(alpha) / c)\ / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b phi = 1 + zeta - 2 * mu psi = eta - 2 * delta kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([1 - phi, 1 - phi, 1 - psi]), 'F_1': np.array([phi, phi - 1, psi]), 'F_2': np.array([1 - phi, -phi, 1 - psi]), 'H': np.array([zeta, zeta, eta]), 'H_1': np.array([1 - zeta, -zeta, 1 - eta]), 'H_2': np.array([-zeta, -zeta, 1 - eta]), 'I': np.array([0.5, -0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'X': np.array([0.5, -0.5, 0.0]), 'Y': np.array([mu, mu, delta]), 'Y_1': np.array([1 - mu, -mu, -delta]), 'Y_2': np.array([-mu, -mu, -delta]), 'Y_3': np.array([mu, mu - 1, delta]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "Y", "F", "H", "Z", "I", "F_1"], ["H_1", "Y_1", "X", "\Gamma", "N"], ["M", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def mclc4(self, a, b, c, alpha): self.name = "MCLC4" mu = (1 + b ** 2 / a ** 2) / 4.0 delta = b * c * cos(alpha) / (2 * a ** 2) zeta = mu - 0.25 + (1 - b * cos(alpha) / c)\ / (4 * sin(alpha) ** 2) eta = 0.5 + 2 * zeta * c * cos(alpha) / b phi = 1 + zeta - 2 * mu psi = eta - 2 * delta kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([1 - phi, 1 - phi, 1 - psi]), 'F_1': np.array([phi, phi - 1, psi]), 'F_2': np.array([1 - phi, -phi, 1 - psi]), 'H': np.array([zeta, zeta, eta]), 'H_1': np.array([1 - zeta, -zeta, 1 - eta]), 'H_2': np.array([-zeta, -zeta, 1 - eta]), 'I': np.array([0.5, -0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'X': np.array([0.5, -0.5, 0.0]), 'Y': np.array([mu, mu, delta]), 'Y_1': np.array([1 - mu, -mu, -delta]), 'Y_2': np.array([-mu, -mu, -delta]), 'Y_3': np.array([mu, mu - 1, delta]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "Y", "F", "H", "Z", "I"], ["H_1", "Y_1", "X", "\Gamma", "N"], ["M", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def mclc5(self, a, b, c, alpha): self.name = "MCLC5" zeta = (b ** 2 / a ** 2 + (1 - b * cos(alpha) / c) / sin(alpha) ** 2) / 4 eta = 0.5 + 2 * zeta * c * cos(alpha) / b mu = eta / 2 + b ** 2 / (4 * a ** 2) \ - b * c * cos(alpha) / (2 * a ** 2) nu = 2 * mu - zeta rho = 1 - zeta * a ** 2 / b ** 2 omega = (4 * nu - 1 - b ** 2 * sin(alpha) ** 2 / a ** 2)\ * c / (2 * b * cos(alpha)) delta = zeta * c * cos(alpha) / b + omega / 2 - 0.25 kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'F': np.array([nu, nu, omega]), 'F_1': np.array([1 - nu, 1 - nu, 1 - omega]), 'F_2': np.array([nu, nu - 1, omega]), 'H': np.array([zeta, zeta, eta]), 'H_1': np.array([1 - zeta, -zeta, 1 - eta]), 'H_2': np.array([-zeta, -zeta, 1 - eta]), 'I': np.array([rho, 1 - rho, 0.5]), 'I_1': np.array([1 - rho, rho - 1, 0.5]), 'L': np.array([0.5, 0.5, 0.5]), 'M': np.array([0.5, 0.0, 0.5]), 'N': np.array([0.5, 0.0, 0.0]), 'N_1': np.array([0.0, -0.5, 0.0]), 'X': np.array([0.5, -0.5, 0.0]), 'Y': np.array([mu, mu, delta]), 'Y_1': np.array([1 - mu, -mu, -delta]), 'Y_2': np.array([-mu, -mu, -delta]), 'Y_3': np.array([mu, mu - 1, delta]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["\Gamma", "Y", "F", "L", "I"], ["I_1", "Z", "H", "F_1"], ["H_1", "Y_1", "X", "\Gamma", "N"], ["M", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def tria(self): self.name = "TRI1a" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'L': np.array([0.5, 0.5, 0.0]), 'M': np.array([0.0, 0.5, 0.5]), 'N': np.array([0.5, 0.0, 0.5]), 'R': np.array([0.5, 0.5, 0.5]), 'X': np.array([0.5, 0.0, 0.0]), 'Y': np.array([0.0, 0.5, 0.0]), 'Z': np.array([0.0, 0.0, 0.5])} path = [["X", "\Gamma", "Y"], ["L", "\Gamma", "Z"], ["N", "\Gamma", "M"], ["R", "\Gamma"]] return {'kpoints': kpoints, 'path': path} def trib(self): self.name = "TRI1b" kpoints = {'\Gamma': np.array([0.0, 0.0, 0.0]), 'L': np.array([0.5, -0.5, 0.0]), 'M': np.array([0.0, 0.0, 0.5]), 'N': np.array([-0.5, -0.5, 0.5]), 'R': np.array([0.0, -0.5, 0.5]), 'X': np.array([0.0, -0.5, 0.0]), 'Y': np.array([0.5, 0.0, 0.0]), 'Z': np.array([-0.5, 0.0, 0.5])} path = [["X", "\Gamma", "Y"], ["L", "\Gamma", "Z"], ["N", "\Gamma", "M"], ["R", "\Gamma"]] return {'kpoints': kpoints, 'path': path}
from mpinterfaces.nanoparticle import Nanoparticle # ----------------------------------- # nanoparticle specifications # ----------------------------------- # max radius in angstroms rmax = 15 # surface families to be chopped off hkl_family = [(1, 0, 0), (1, 1, 1)] # surfac energies could be in any units, will be normalized surface_energies = [28, 25] # ----------------------------------- # initial structure # ----------------------------------- # caution: set the structure wrt which the the miller indices are # specified. use your own key structure = get_struct_from_mp('PbS', MAPI_KEY="") # primitive ---> conventional cell sa = SpacegroupAnalyzer(structure) structure_conventional = sa.get_conventional_standard_structure() # ----------------------------------- # create nanoparticle # ----------------------------------- nanoparticle = Nanoparticle(structure_conventional, rmax=rmax, hkl_family=hkl_family, surface_energies=surface_energies) nanoparticle.create() nanoparticle.to(fmt='xyz', filename='nanoparticle.xyz')
def conventional_structure(self, structure): sga = SpacegroupAnalyzer(structure) return sga.get_conventional_standard_structure()