def setUp(self): for virtvar in ['boundaries', 'celltype']: assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar # Basic unit cell information: pbc_c = {'zero' : (False,False,False), \ 'periodic': (True,True,True), \ 'mixed' : (True, False, True)}[self.boundaries] a, b = self.a, 2**0.5*self.a cell_cv = {'general' : np.array([[0,a,a],[a/2,0,a/2],[a/2,a/2,0]]), 'rotated' : np.array([[0,0,b],[b/2,0,0],[0,b/2,0]]), 'inverted' : np.array([[0,0,b],[0,b/2,0],[b/2,0,0]]), 'orthogonal': np.diag([b, b/2, b/2])}[self.celltype] cell_cv = np.array([(4-3*pbc)*c_v for pbc,c_v in zip(pbc_c, cell_cv)]) # Decide how many kpoints to sample from the 1st Brillouin Zone kpts_c = np.ceil((10/Bohr)/np.sum(cell_cv**2,axis=1)**0.5).astype(int) kpts_c = tuple(kpts_c*pbc_c + 1-pbc_c) bzk_kc = kpts2ndarray(kpts_c) self.gamma = len(bzk_kc) == 1 and not bzk_kc[0].any() #p = InputParameters() #Z_a = self.atoms.get_atomic_numbers() #xcfunc = XC(p.xc) #setups = Setups(Z_a, p.setups, p.basis, p.lmax, xcfunc) #symmetry, weight_k, self.ibzk_kc = reduce_kpoints(self.atoms, bzk_kc, # setups, p.usesymm) self.ibzk_kc = bzk_kc.copy() # don't use symmetry reduction of kpoints self.nibzkpts = len(self.ibzk_kc) self.ibzk_kv = kpoint_convert(cell_cv, skpts_kc=self.ibzk_kc) # Parse parallelization parameters and create suitable communicators. #parsize_domain, parsize_bands = create_parsize_minbands(self.nbands, world.size) parsize_domain, parsize_bands = world.size//gcd(world.size, self.nibzkpts), 1 assert self.nbands % np.prod(parsize_bands) == 0 domain_comm, kpt_comm, band_comm = distribute_cpus(parsize_domain, parsize_bands, self.nspins, self.nibzkpts) # Set up band descriptor: self.bd = BandDescriptor(self.nbands, band_comm) # Set up grid descriptor: N_c = np.round(np.sum(cell_cv**2, axis=1)**0.5 / self.h) N_c += 4-N_c % 4 # makes domain decomposition easier self.gd = GridDescriptor(N_c, cell_cv, pbc_c, domain_comm, parsize_domain) self.assertEqual(self.gamma, np.all(~self.gd.pbc_c)) # What to do about kpoints? self.kpt_comm = kpt_comm if debug and world.rank == 0: comm_sizes = tuple([comm.size for comm in [world, self.bd.comm, \ self.gd.comm, self.kpt_comm]]) print '%d world, %d band, %d domain, %d kpt' % comm_sizes
def setUp(self): for virtvar in ['boundaries', 'celltype']: assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar # Basic unit cell information: pbc_c = {'zero' : (False,False,False), \ 'periodic': (True,True,True), \ 'mixed' : (True, False, True)}[self.boundaries] a, b = self.a, 2**0.5*self.a cell_cv = {'general' : np.array([[0,a,a],[a/2,0,a/2],[a/2,a/2,0]]), 'rotated' : np.array([[0,0,b],[b/2,0,0],[0,b/2,0]]), 'inverted' : np.array([[0,0,b],[0,b/2,0],[b/2,0,0]]), 'orthogonal': np.diag([b, b/2, b/2])}[self.celltype] cell_cv = np.array([(4-3*pbc)*c_v for pbc,c_v in zip(pbc_c, cell_cv)]) # Decide how many kpoints to sample from the 1st Brillouin Zone kpts_c = np.ceil((10/Bohr)/np.sum(cell_cv**2,axis=1)**0.5).astype(int) kpts_c = tuple(kpts_c*pbc_c + 1-pbc_c) bzk_kc = kpts2ndarray(kpts_c) self.gamma = len(bzk_kc) == 1 and not bzk_kc[0].any() #p = InputParameters() #Z_a = self.atoms.get_atomic_numbers() #xcfunc = XC(p.xc) #setups = Setups(Z_a, p.setups, p.basis, p.lmax, xcfunc) #symmetry, weight_k, self.ibzk_kc = reduce_kpoints(self.atoms, bzk_kc, # setups, p.usesymm) self.ibzk_kc = bzk_kc.copy() # don't use symmetry reduction of kpoints self.nibzkpts = len(self.ibzk_kc) self.ibzk_kv = kpoint_convert(cell_cv, skpts_kc=self.ibzk_kc) # Parse parallelization parameters and create suitable communicators. #parsize, parsize_bands = create_parsize_minbands(self.nbands, world.size) parsize, parsize_bands = world.size//gcd(world.size, self.nibzkpts), 1 assert self.nbands % np.prod(parsize_bands) == 0 domain_comm, kpt_comm, band_comm = distribute_cpus(parsize, parsize_bands, self.nspins, self.nibzkpts) # Set up band descriptor: self.bd = BandDescriptor(self.nbands, band_comm) # Set up grid descriptor: N_c = np.round(np.sum(cell_cv**2, axis=1)**0.5 / self.h) N_c += 4-N_c % 4 # makes domain decomposition easier self.gd = GridDescriptor(N_c, cell_cv, pbc_c, domain_comm, parsize) self.assertEqual(self.gamma, np.all(~self.gd.pbc_c)) # What to do about kpoints? self.kpt_comm = kpt_comm if debug and world.rank == 0: comm_sizes = tuple([comm.size for comm in [world, self.bd.comm, \ self.gd.comm, self.kpt_comm]]) print '%d world, %d band, %d domain, %d kpt' % comm_sizes
def get_bandpath_fcc(ase_atom, npoints=30): # Set-up the band-path via special points from ase.dft.kpoints import ibz_points, kpoint_convert, get_bandpath points = ibz_points['fcc'] G = points['Gamma'] X = points['X'] W = points['W'] K = points['K'] L = points['L'] kpts_reduced, kpath, sp_points = get_bandpath([L, G, X, W, K, G], ase_atom.cell, npoints=npoints) kpts_cartes = kpoint_convert(ase_atom.cell, skpts_kc=kpts_reduced) return kpts_reduced, kpts_cartes, kpath, sp_points
def get_bandpath_fcc(ase_atom, npoints=30): # Set-up the band-path via special points from ase.dft.kpoints import ibz_points, kpoint_convert, get_bandpath points = ibz_points["fcc"] G = points["Gamma"] X = points["X"] W = points["W"] K = points["K"] L = points["L"] kpts_reduced, kpath, sp_points = get_bandpath([L, G, X, W, K, G], ase_atom.cell, npoints=npoints) kpts_cartes = kpoint_convert(ase_atom.cell, skpts_kc=kpts_reduced) return kpts_reduced, kpts_cartes, kpath, sp_points
def get_bandpath_as_aims_strings(self, pbc=[True, True, True]): """This function sets up the band path according to Setyawan-Curtarolo conventions. Returns: list: List of strings containing the k-path sections. """ from ase.dft.kpoints import parse_path_string, kpoint_convert atoms = self.structure.atoms atoms.pbc = pbc path = parse_path_string( atoms.cell.get_bravais_lattice(pbc=atoms.pbc).bandpath().path) # list Of lists of path segments points = atoms.cell.get_bravais_lattice( pbc=atoms.pbc).bandpath().special_points segments = [] for seg in path: section = [(i, j) for i, j in zip(seg[:-1], seg[1:])] segments.append(section) output_bands = [] index = 1 for seg in segments: output_bands.append( "## Brillouin Zone section Nr. {:d}\n".format(index)) for sec in seg: dist = np.array(points[sec[1]]) - np.array(points[sec[0]]) length = np.linalg.norm( kpoint_convert(atoms.cell, skpts_kc=dist)) npoints = np.int_(np.round(np.asarray(length) * 20)) vec1 = "{:.6f} {:.6f} {:.6f}".format(*points[sec[0]]) vec2 = "{:.6f} {:.6f} {:.6f}".format(*points[sec[1]]) output_bands.append( "{vec1} \t {vec2} \t {npoints} \t {label1} {label2}". format( label1=sec[0], label2=sec[1], npoints=npoints, vec1=vec1, vec2=vec2, )) index += 1 return output_bands
def read_pw_out(fileobj, index=-1, results_required=True): """Reads Quantum ESPRESSO output files. The atomistic configurations as well as results (energy, force, stress, magnetic moments) of the calculation are read for all configurations within the output file. Will probably raise errors for broken or incomplete files. Parameters ---------- fileobj : file|str A file like object or filename index : slice The index of configurations to extract. results_required : bool If True, atomistic configurations that do not have any associated results will not be included. This prevents double printed configurations and incomplete calculations from being returned as the final configuration with no results data. Yields ------ structure : Atoms The next structure from the index slice. The Atoms has a SinglePointCalculator attached with any results parsed from the file. """ if isinstance(fileobj, str): fileobj = open(fileobj, 'rU') # work with a copy in memory for faster random access pwo_lines = fileobj.readlines() # TODO: index -1 special case? # Index all the interesting points indexes = { _PW_START: [], _PW_END: [], _PW_CELL: [], _PW_POS: [], _PW_MAGMOM: [], _PW_FORCE: [], _PW_TOTEN: [], _PW_STRESS: [], _PW_FERMI: [], _PW_HIGHEST_OCCUPIED: [], _PW_HIGHEST_OCCUPIED_LOWEST_FREE: [], _PW_KPTS: [], _PW_BANDS: [], _PW_BANDSTRUCTURE: [], _PW_ELECTROSTATIC_EMBEDDING: [], _PW_NITER: [], _PW_DONE: [], _PW_WALLTIME: [] } for idx, line in enumerate(pwo_lines): for identifier in indexes: if identifier in line: indexes[identifier].append(idx) # Configurations are either at the start, or defined in ATOMIC_POSITIONS # in a subsequent step. Can deal with concatenated output files. all_config_indexes = sorted(indexes[_PW_START] + indexes[_PW_POS]) # Slice only requested indexes # setting results_required argument stops configuration-only # structures from being returned. This ensures the [-1] structure # is one that has results. Two cases: # - SCF of last configuration is not converged, job terminated # abnormally. # - 'relax' and 'vc-relax' re-prints the final configuration but # only 'vc-relax' recalculates. if results_required: results_indexes = sorted(indexes[_PW_TOTEN] + indexes[_PW_FORCE] + indexes[_PW_STRESS] + indexes[_PW_MAGMOM] + indexes[_PW_BANDS] + indexes[_PW_ELECTROSTATIC_EMBEDDING] + indexes[_PW_BANDSTRUCTURE]) # Prune to only configurations with results data before the next # configuration results_config_indexes = [] for config_index, config_index_next in zip( all_config_indexes, all_config_indexes[1:] + [len(pwo_lines)]): if any([ config_index < results_index < config_index_next for results_index in results_indexes ]): results_config_indexes.append(config_index) # slice from the subset image_indexes = results_config_indexes[index] else: image_indexes = all_config_indexes[index] # Extract initialisation information each time PWSCF starts # to add to subsequent configurations. Use None so slices know # when to fill in the blanks. pwscf_start_info = dict((idx, None) for idx in indexes[_PW_START]) if isinstance(image_indexes, int): image_indexes = [image_indexes] for image_index in image_indexes: # Find the nearest calculation start to parse info. Needed in, # for example, relaxation where cell is only printed at the # start. if image_index in indexes[_PW_START]: prev_start_index = image_index else: # The greatest start index before this structure prev_start_index = [ idx for idx in indexes[_PW_START] if idx < image_index ][-1] # add structure to reference if not there if pwscf_start_info[prev_start_index] is None: pwscf_start_info[prev_start_index] = parse_pwo_start( pwo_lines, prev_start_index) # Get the bounds for information for this structure. Any associated # values will be between the image_index and the following one, # EXCEPT for cell, which will be 4 lines before if it exists. for next_index in all_config_indexes: if next_index > image_index: break else: # right to the end of the file next_index = len(pwo_lines) # Get the structure # Use this for any missing data prev_structure = pwscf_start_info[prev_start_index]['atoms'] if image_index in indexes[_PW_START]: structure = prev_structure.copy() # parsed from start info else: if _PW_CELL in pwo_lines[image_index - 5]: # CELL_PARAMETERS would be just before positions if present cell, cell_alat = get_cell_parameters(pwo_lines[image_index - 5:image_index]) else: cell = prev_structure.cell cell_alat = pwscf_start_info[prev_start_index]['alat'] # give at least enough lines to parse the positions # should be same format as input card n_atoms = len(prev_structure) positions_card = get_atomic_positions( pwo_lines[image_index:image_index + n_atoms + 1], n_atoms=n_atoms, cell=cell, alat=cell_alat) # convert to Atoms object symbols = [ label_to_symbol(position[0]) for position in positions_card ] tags = [label_to_tag(position[0]) for position in positions_card] positions = [position[1] for position in positions_card] constraint_idx = [position[2] for position in positions_card] constraint = get_constraint(constraint_idx) structure = Atoms(symbols=symbols, positions=positions, cell=cell, constraint=constraint, pbc=True, tags=tags) # Extract calculation results # Energy energy = None for energy_index in indexes[_PW_TOTEN]: if image_index < energy_index < next_index: energy = float( pwo_lines[energy_index].split()[-2]) * units['Ry'] # Electrostatic enbedding energy elec_embedding_energy = None for eee_index in indexes[_PW_ELECTROSTATIC_EMBEDDING]: if image_index < eee_index < next_index: elec_embedding_energy = float( pwo_lines[eee_index].split()[-2]) * units['Ry'] # Number of iterations n_iterations = None for niter_index in indexes[_PW_NITER]: if image_index < niter_index < next_index: n_iterations = int( pwo_lines[niter_index].split('#')[1].split()[0]) # Forces forces = None for force_index in indexes[_PW_FORCE]: if image_index < force_index < next_index: # Before QE 5.3 'negative rho' added 2 lines before forces # Use exact lines to stop before 'non-local' forces # in high verbosity if not pwo_lines[force_index + 2].strip(): force_index += 4 else: force_index += 2 # assume contiguous forces = [[float(x) for x in force_line.split()[-3:]] for force_line in pwo_lines[force_index:force_index + len(structure)]] forces = np.array(forces) * units['Ry'] / units['Bohr'] # Stress stress = None for stress_index in indexes[_PW_STRESS]: if image_index < stress_index < next_index: sxx, sxy, sxz = pwo_lines[stress_index + 1].split()[:3] _, syy, syz = pwo_lines[stress_index + 2].split()[:3] _, _, szz = pwo_lines[stress_index + 3].split()[:3] stress = np.array([sxx, syy, szz, syz, sxz, sxy], dtype=float) # sign convention is opposite of ase stress *= -1 * units['Ry'] / (units['Bohr']**3) # Magmoms magmoms = None for magmoms_index in indexes[_PW_MAGMOM]: if image_index < magmoms_index < next_index: magmoms = [ float(mag_line.split('=')[-1]) for mag_line in pwo_lines[magmoms_index + 1:magmoms_index + 1 + len(structure)] ] # Fermi level / highest occupied level and lowest unoccupied level efermi = None lumo_ene = None for fermi_index in indexes[_PW_FERMI]: if image_index < fermi_index < next_index: efermi = float(pwo_lines[fermi_index].split()[-2]) if efermi is None: for ho_index in indexes[_PW_HIGHEST_OCCUPIED]: if image_index < ho_index < next_index: efermi = float(pwo_lines[ho_index].split()[-1]) if efermi is None: for holf_index in indexes[_PW_HIGHEST_OCCUPIED_LOWEST_FREE]: if image_index < holf_index < next_index: efermi = float(pwo_lines[holf_index].split()[-2]) lumo_ene = float(pwo_lines[holf_index].split()[-1]) # K-points ibzkpts = None weights = None kpoints_warning = "Number of k-points >= 100: " + \ "set verbosity='high' to print them." for kpts_index in indexes[_PW_KPTS]: nkpts = int(pwo_lines[kpts_index].split()[4]) kpts_index += 2 if pwo_lines[kpts_index].strip() == kpoints_warning: continue # QE prints the k-points in units of 2*pi/alat # with alat defined as the length of the first # cell vector cell = structure.get_cell() alat = np.linalg.norm(cell[0]) ibzkpts = [] weights = [] for i in range(nkpts): L = pwo_lines[kpts_index + i].split() weights.append(float(L[-1])) coord = np.array([L[-6], L[-5], L[-4].strip('),')], dtype=float) coord *= 2 * np.pi / alat coord = kpoint_convert(cell, ckpts_kv=coord) ibzkpts.append(coord) ibzkpts = np.array(ibzkpts) weights = np.array(weights) # Bands kpts = None kpoints_warning = "Number of k-points >= 100: " + \ "set verbosity='high' to print the bands." for bands_index in indexes[_PW_BANDS] + indexes[_PW_BANDSTRUCTURE]: if image_index < bands_index < next_index: bands_index += 2 if pwo_lines[bands_index].strip() == kpoints_warning: continue assert ibzkpts is not None spin, bands, eigenvalues = 0, [], [[], []] while True: L = pwo_lines[bands_index].replace('-', ' -').split() if len(L) == 0: if len(bands) > 0: eigenvalues[spin].append(bands) bands = [] elif L == ['occupation', 'numbers']: # Skip the lines with the occupation numbers bands_index += len(eigenvalues[spin][0]) // 8 + 1 elif L[0] == 'k' and L[1].startswith('='): pass elif 'SPIN' in L: if 'DOWN' in L: spin += 1 else: try: bands.extend(map(float, L)) except ValueError: break bands_index += 1 if spin == 1: assert len(eigenvalues[0]) == len(eigenvalues[1]) # assert len(eigenvalues[0]) == len(ibzkpts), \ # (np.shape(eigenvalues), len(ibzkpts)) kpts = [] for s in range(spin + 1): for w, k, e in zip(weights, ibzkpts, eigenvalues[s]): kpt = SinglePointKPoint(w, s, k, eps_n=e) kpts.append(kpt) # Convergence job_done = False for done_index in indexes[_PW_DONE]: if image_index < done_index < next_index: job_done = True # Walltime walltime = None for wt_index in indexes[_PW_WALLTIME]: if image_index < wt_index < next_index: walltime = time_to_float(pwo_lines[wt_index].split()[-2]) # Put everything together calc = SinglePointDFTCalculator(structure, energy=energy, forces=forces, stress=stress, magmoms=magmoms, efermi=efermi, ibzkpts=ibzkpts) calc.results['homo_energy'] = efermi calc.results['lumo_energy'] = lumo_ene calc.results['electrostatic embedding'] = elec_embedding_energy calc.results['iterations'] = n_iterations calc.results['job done'] = job_done calc.results['walltime'] = walltime calc.kpts = kpts structure.calc = calc yield structure
def plot_bands(scftype, basis, ngs, nmp=None): # Set-up the unit cell from ase.lattice import bulk from ase.dft.kpoints import ibz_points, kpoint_convert, get_bandpath ase_atom = bulk('C', 'diamond', a=3.5668*ANG2BOHR) print "Cell volume =", ase_atom.get_volume(), "Bohr^3" # Set-up the band-path via special points points = ibz_points['fcc'] G = points['Gamma'] X = points['X'] W = points['W'] K = points['K'] L = points['L'] band_kpts, x, X = get_bandpath([L, G, X, W, K, G], ase_atom.cell, npoints=30) abs_kpts = kpoint_convert(ase_atom.cell, skpts_kc=band_kpts) # Build the cell cell = pbcgto.Cell() cell.unit = 'B' cell.atom = pyscf_ase.ase_atoms_to_pyscf(ase_atom) cell.h = ase_atom.cell cell.basis = 'gth-%s'%(basis) #cell.basis = 'gth-szv' #cell.basis = 'gth-dzvp' cell.pseudo = 'gth-pade' cell.gs = np.array([ngs,ngs,ngs]) cell.verbose = 7 cell.build(None,None) # Perform the gamma-point SCF if scftype == 'dft': mf = pbcdft.RKS(cell) mf.xc = 'lda,vwn' elif scftype == 'hf': mf = pbchf.RHF(cell, exxdiv=None) else: scaled_mp_kpts = ase.dft.kpoints.monkhorst_pack((nmp,nmp,nmp)) abs_mp_kpts = cell.get_abs_kpts(scaled_mp_kpts) if scftype == 'kdft': mf = pbcdft.KRKS(cell, abs_mp_kpts) mf.xc = 'lda,vwn' else: mf = pbchf.KRHF(cell, abs_mp_kpts, exxdiv='vcut_sph') mf.analytic_int = False mf.scf() print "SCF evals =", mf.mo_energy # Proceed along k-point band-path e_kn = [] efermi = -99 for kpt in abs_kpts: e, c = mf.get_bands(kpt) print kpt, e e_kn.append(e) if e[4-1] > efermi: efermi = e[4-1] for k, ek in enumerate(e_kn): e_kn[k] = ek-efermi # Write the bands to stdout f = open('bands_%s_%s_%d_%d.dat'%(scftype,basis,ngs,nmp),'w') f.write("# Special points:\n") for point, label in zip(X,['L', 'G', 'X', 'W', 'K', 'G']): f.write("# %0.6f %s\n"%(point,label)) for kk, ek in zip(x, e_kn): f.write("%0.6f "%(kk)) for ekn in ek: f.write("%0.6f "%(ekn)) f.write("\n") f.close() # Plot the band structure via matplotlib emin = -1.0 emax = 1.0 plt.figure(figsize=(8, 4)) nbands = cell.nao_nr() for n in range(nbands): plt.plot(x, [e_kn[i][n] for i in range(len(x))]) for p in X: plt.plot([p, p], [emin, emax], 'k-') plt.plot([0, X[-1]], [0, 0], 'k-') plt.xticks(X, ['$%s$' % n for n in ['L', r'\Gamma', 'X', 'W', 'K', r'\Gamma']]) plt.axis(xmin=0, xmax=X[-1], ymin=emin, ymax=emax) plt.xlabel('k-vector') plt.ylabel('Energy [au]') #plt.show() if nmp is None: plt.savefig('bands_%s_%s_%d.png'%(scftype,basis,ngs)) else: plt.savefig('bands_%s_%s_%d_%d.png'%(scftype,basis,ngs,nmp))