def get_transport_kpts(bzk_kc, dir='x'): from ase.dft.kpoints import get_monkhorst_pack_size_and_offset, \ monkhorst_pack dir = 'xyz'.index(direction) transverse_dirs = np.delete([0, 1, 2], [dir]) dtype = float if len(bzk_kc) > 1 or np.any(bzk_kc[0] != [0, 0, 0]): dtype = complex kpts_grid, kpts_shift = get_monkhorst_pack_size_and_offset(bzk_kc) # kpts in the transport direction nkpts_p = kpts_grid[dir] bzk_p_kc = monkhorst_pack((nkpts_p, 1, 1))[:, 0] + kpts_shift[dir] weight_p_k = 1. / nkpts_p # kpts in the transverse directions offset = np.zeros((3, )) offset[:len(transverse_dirs)] = kpts_shift[transverse_dirs] bzk_t_kc = monkhorst_pack(tuple(kpts_grid[transverse_dirs]) + (1, )) + offset return (bzk_p_kc, weight_p_k), (bzk_t_kc, )
def atoms2bandstructure(atoms, parser, args): cell = atoms.get_cell() calc = atoms.calc bzkpts = calc.get_bz_k_points() ibzkpts = calc.get_ibz_k_points() efermi = calc.get_fermi_level() nibz = len(ibzkpts) nspins = 1 + int(calc.get_spin_polarized()) eps = np.array([[calc.get_eigenvalues(kpt=k, spin=s) for k in range(nibz)] for s in range(nspins)]) if not args.quiet: print('Spins, k-points, bands: {}, {}, {}'.format(*eps.shape)) if bzkpts is None: if ibzkpts is None: raise ValueError('Cannot find any k-point data') else: path_kpts = ibzkpts else: try: size, offset = get_monkhorst_pack_size_and_offset(bzkpts) except ValueError: path_kpts = ibzkpts else: if not args.quiet: print('Interpolating from Monkhorst-Pack grid (size, offset):') print(size, offset) if args.path is None: err = 'Please specify a path!' try: cs = crystal_structure_from_cell(cell) except ValueError: err += ('\nASE cannot automatically ' 'recognize this crystal structure') else: from ase.dft.kpoints import special_paths kptpath = special_paths[cs] err += ('\nIt looks like you have a {} crystal structure.' '\nMaybe you want its special path:' ' {}'.format(cs, kptpath)) parser.error(err) bz2ibz = calc.get_bz_to_ibz_map() path_kpts = bandpath(args.path, atoms.cell, args.points).kpts icell = atoms.get_reciprocal_cell() eps = monkhorst_pack_interpolate(path_kpts, eps.transpose(1, 0, 2), icell, bz2ibz, size, offset) eps = eps.transpose(1, 0, 2) special_points = get_special_points(cell) path = BandPath(atoms.cell, kpts=path_kpts, special_points=special_points) return BandStructure(path, eps, reference=efermi)
def atoms2bandpath(atoms, path='default', k_points=False, ibz_k_points=False, dimension=3, verbose=False): cell = atoms.get_cell() icell = atoms.get_reciprocal_cell() try: cs = crystal_structure_from_cell(cell) except ValueError: cs = None if verbose: if cs: print('Crystal:', cs) print('Special points:', special_paths[cs]) print('Lattice vectors:') for i, v in enumerate(cell): print('{}: ({:16.9f},{:16.9f},{:16.9f})'.format(i + 1, *v)) print('Reciprocal vectors:') for i, v in enumerate(icell): print('{}: ({:16.9f},{:16.9f},{:16.9f})'.format(i + 1, *v)) # band path special_points = None if path: if path == 'default': path = special_paths[cs] paths = [] special_points = get_special_points(cell) for names in parse_path_string(path): points = [] for name in names: points.append(np.dot(icell.T, special_points[name])) paths.append((names, points)) else: paths = None # k points points = None if atoms.calc is not None and hasattr(atoms.calc, 'get_bz_k_points'): bzk = atoms.calc.get_bz_k_points() if path is None: try: size, offset = get_monkhorst_pack_size_and_offset(bzk) except ValueError: # This was not a MP-grid. Must be a path in the BZ: path = ''.join(labels_from_kpts(bzk, cell)[2]) if k_points: points = bzk elif ibz_k_points: points = atoms.calc.get_ibz_k_points() return BandPath(cell, kpts=points, special_points=special_points)
def __init__(self, calc, width=0.1, window=None, npts=401, comm=world): """Electronic Density Of States object. calc: calculator object Any ASE compliant calculator object. width: float Width of guassian smearing. Use width=0.0 for linear tetrahedron interpolation. window: tuple of two float Use ``window=(emin, emax)``. If not specified, a window big enough to hold all the eigenvalues will be used. npts: int Number of points. comm: communicator object MPI communicator for lti_dos """ self.comm = comm self.npts = npts self.width = width self.w_k = calc.get_k_point_weights() self.nspins = calc.get_number_of_spins() self.e_skn = np.array([[ calc.get_eigenvalues(kpt=k, spin=s) for k in range(len(self.w_k)) ] for s in range(self.nspins)]) try: # two Fermi levels for i, eF in enumerate(calc.get_fermi_level()): self.e_skn[i] -= eF except TypeError: # a single Fermi level self.e_skn -= calc.get_fermi_level() if window is None: emin = None emax = None else: emin, emax = window if emin is None: emin = self.e_skn.min() - 5 * self.width if emax is None: emax = self.e_skn.max() + 5 * self.width self.energies = np.linspace(emin, emax, npts) if width == 0.0: bzkpts = calc.get_bz_k_points() size, offset = get_monkhorst_pack_size_and_offset(bzkpts) bz2ibz = calc.get_bz_to_ibz_map() shape = (self.nspins, ) + tuple(size) + (-1, ) self.e_skn = self.e_skn[:, bz2ibz].reshape(shape) self.cell = calc.atoms.cell
def get_realspace_hs(h_skmm, s_kmm, bzk_kc, weight_k): kpts_grid, kpts_shift = get_monkhorst_pack_size_and_offset(bzk_kc) R_rc = get_realspace_R(kpts_grid // 2) ibzk_kc = reduce_vecs(bzk_kc.tolist()) h_srii = np.zeros_like(h_skmm) s_rii = np.zeros_like(s_kmm) for r, R_c in enumerate(R_rc): for k, ibzk_c in enumerate(ibzk_kc): c_k = np.exp(2.j * np.pi * np.dot(ibzk_c, R_c)) * weight_k[k] h_srii[:, r] += c_k * h_skmm[:, k] s_rii[r] += c_k * s_kmm[k] return h_srii, s_rii, R_rc
def main(args, parser): atoms = read(args.calculation) cell = atoms.get_cell() calc = atoms.calc bzkpts = calc.get_bz_k_points() ibzkpts = calc.get_ibz_k_points() efermi = calc.get_fermi_level() nibz = len(ibzkpts) nspins = 1 + int(calc.get_spin_polarized()) eps = np.array([[calc.get_eigenvalues(kpt=k, spin=s) for k in range(nibz)] for s in range(nspins)]) if not args.quiet: print('Spins, k-points, bands: {}, {}, {}'.format(*eps.shape)) try: size, offset = get_monkhorst_pack_size_and_offset(bzkpts) except ValueError: path = ibzkpts else: if not args.quiet: print('Interpolating from Monkhorst-Pack grid (size, offset):') print(size, offset) if args.path is None: err = 'Please specify a path!' try: cs = crystal_structure_from_cell(cell) except ValueError: err += ('\nGPAW cannot autimatically ' 'recognize this crystal structure') else: from ase.dft.kpoints import special_paths kptpath = special_paths[cs] err += ('\nIt looks like you have a {} crystal structure.' '\nMaybe you want its special path:' ' {}'.format(cs, kptpath)) parser.error(err) bz2ibz = calc.get_bz_to_ibz_map() path = bandpath(args.path, atoms.cell, args.points)[0] icell = atoms.get_reciprocal_cell() eps = monkhorst_pack_interpolate(path, eps.transpose(1, 0, 2), icell, bz2ibz, size, offset) eps = eps.transpose(1, 0, 2) emin, emax = (float(e) for e in args.range) bs = BandStructure(atoms.cell, path, eps, reference=efermi) bs.plot(emin=emin, emax=emax)
def get_realspace_hs(h_skmm, s_kmm, bzk_kc, weight_k, R_c=(0, 0, 0), direction='x', usesymm=None): from gpaw.symmetry import Symmetry from ase.dft.kpoints import get_monkhorst_pack_size_and_offset, \ monkhorst_pack if usesymm is True: raise NotImplementedError, 'Only None and False have been implemented' nspins, nk, nbf = h_skmm.shape[:3] dir = 'xyz'.index(direction) transverse_dirs = np.delete([0, 1, 2], [dir]) dtype = float if len(bzk_kc) > 1 or np.any(bzk_kc[0] != [0, 0, 0]): dtype = complex kpts_grid = get_monkhorst_pack_size_and_offset(bzk_kc)[0] # kpts in the transport direction nkpts_p = kpts_grid[dir] bzk_p_kc = monkhorst_pack((nkpts_p,1,1))[:, 0] weight_p_k = 1. / nkpts_p # kpts in the transverse directions bzk_t_kc = monkhorst_pack(tuple(kpts_grid[transverse_dirs]) + (1, )) if usesymm == False: #XXX a somewhat ugly hack: # By default GPAW reduces inversion sym in the z direction. The steps # below assure reduction in the transverse dirs. # For now this part seems to do the job, but it may be written # in a smarter way in the future. symmetry = Symmetry([1], np.eye(3)) symmetry.prune_symmetries(np.zeros((1, 3))) ibzk_kc, ibzweight_k = symmetry.reduce(bzk_kc)[:2] ibzk_t_kc, weights_t_k = symmetry.reduce(bzk_t_kc)[:2] ibzk_t_kc = ibzk_t_kc[:, :2] nkpts_t = len(ibzk_t_kc) else: # usesymm = None ibzk_kc = bzk_kc.copy() ibzk_t_kc = bzk_t_kc nkpts_t = len(bzk_t_kc) weights_t_k = [1. / nkpts_t for k in range(nkpts_t)] h_skii = np.zeros((nspins, nkpts_t, nbf, nbf), dtype) if s_kmm is not None: s_kii = np.zeros((nkpts_t, nbf, nbf), dtype) tol = 7 for j, k_t in enumerate(ibzk_t_kc): for k_p in bzk_p_kc: k = np.zeros((3,)) k[dir] = k_p k[transverse_dirs] = k_t kpoint_list = [list(np.round(k_kc, tol)) for k_kc in ibzk_kc] if list(np.round(k, tol)) not in kpoint_list: k = -k # inversion index = kpoint_list.index(list(np.round(k,tol))) h = h_skmm[:, index].conjugate() if s_kmm is not None: s = s_kmm[index].conjugate() k=-k else: # kpoint in the ibz index = kpoint_list.index(list(np.round(k, tol))) h = h_skmm[:, index] if s_kmm is not None: s = s_kmm[index] c_k = np.exp(2.j * np.pi * np.dot(k, R_c)) * weight_p_k h_skii[:, j] += c_k * h if s_kmm is not None: s_kii[j] += c_k * s if s_kmm is None: return ibzk_t_kc, weights_t_k, h_skii else: return ibzk_t_kc, weights_t_k, h_skii, s_kii
def __init__( self, nwannier, calc, file=None, nbands=None, fixedenergy=None, fixedstates=None, spin=0, initialwannier="random", seed=None, verbose=False, ): """ Required arguments: ``nwannier``: The number of Wannier functions you wish to construct. This must be at least half the number of electrons in the system and at most equal to the number of bands in the calculation. ``calc``: A converged DFT calculator class. If ``file`` arg. is not provided, the calculator *must* provide the method ``get_wannier_localization_matrix``, and contain the wavefunctions (save files with only the density is not enough). If the localization matrix is read from file, this is not needed, unless ``get_function`` or ``write_cube`` is called. Optional arguments: ``nbands``: Bands to include in localization. The number of bands considered by Wannier can be smaller than the number of bands in the calculator. This is useful if the highest bands of the DFT calculation are not well converged. ``spin``: The spin channel to be considered. The Wannier code treats each spin channel independently. ``fixedenergy`` / ``fixedstates``: Fixed part of Heilbert space. Determine the fixed part of Hilbert space by either a maximal energy *or* a number of bands (possibly a list for multiple k-points). Default is None meaning that the number of fixed states is equated to ``nwannier``. ``file``: Read localization and rotation matrices from this file. ``initialwannier``: Initial guess for Wannier rotation matrix. Can be 'bloch' to start from the Bloch states, 'random' to be randomized, or a list passed to calc.get_initial_wannier. ``seed``: Seed for random ``initialwannier``. ``verbose``: True / False level of verbosity. """ # Bloch phase sign convention sign = -1 classname = calc.__class__.__name__ if classname in ["Dacapo", "Jacapo"]: print "Using " + classname sign = +1 self.nwannier = nwannier self.calc = calc self.spin = spin self.verbose = verbose self.kpt_kc = calc.get_bz_k_points() assert len(calc.get_ibz_k_points()) == len(self.kpt_kc) self.kptgrid = get_monkhorst_pack_size_and_offset(self.kpt_kc)[0] self.kpt_kc *= sign self.Nk = len(self.kpt_kc) self.unitcell_cc = calc.get_atoms().get_cell() self.largeunitcell_cc = (self.unitcell_cc.T * self.kptgrid).T self.weight_d, self.Gdir_dc = calculate_weights(self.largeunitcell_cc) self.Ndir = len(self.weight_d) # Number of directions if nbands is not None: self.nbands = nbands else: self.nbands = calc.get_number_of_bands() if fixedenergy is None: if fixedstates is None: self.fixedstates_k = np.array([nwannier] * self.Nk, int) else: if type(fixedstates) is int: fixedstates = [fixedstates] * self.Nk self.fixedstates_k = np.array(fixedstates, int) else: # Setting number of fixed states and EDF from specified energy. # All states below this energy (relative to Fermi level) are fixed. fixedenergy += calc.get_fermi_level() print fixedenergy self.fixedstates_k = np.array( [calc.get_eigenvalues(k, spin).searchsorted(fixedenergy) for k in range(self.Nk)], int ) self.edf_k = self.nwannier - self.fixedstates_k if verbose: print "Wannier: Fixed states : %s" % self.fixedstates_k print "Wannier: Extra degrees of freedom: %s" % self.edf_k # Set the list of neighboring k-points k1, and the "wrapping" k0, # such that k1 - k - G + k0 = 0 # # Example: kpoints = (-0.375,-0.125,0.125,0.375), dir=0 # G = [0.25,0,0] # k=0.375, k1= -0.375 : -0.375-0.375-0.25 => k0=[1,0,0] # # For a gamma point calculation k1 = k = 0, k0 = [1,0,0] for dir=0 if self.Nk == 1: self.kklst_dk = np.zeros((self.Ndir, 1), int) k0_dkc = self.Gdir_dc.reshape(-1, 1, 3) else: self.kklst_dk = np.empty((self.Ndir, self.Nk), int) k0_dkc = np.empty((self.Ndir, self.Nk, 3), int) # Distance between kpoints kdist_c = np.empty(3) for c in range(3): # make a sorted list of the kpoint values in this direction slist = np.argsort(self.kpt_kc[:, c], kind="mergesort") skpoints_kc = np.take(self.kpt_kc, slist, axis=0) kdist_c[c] = max([skpoints_kc[n + 1, c] - skpoints_kc[n, c] for n in range(self.Nk - 1)]) for d, Gdir_c in enumerate(self.Gdir_dc): for k, k_c in enumerate(self.kpt_kc): # setup dist vector to next kpoint G_c = np.where(Gdir_c > 0, kdist_c, 0) if max(G_c) < 1e-4: self.kklst_dk[d, k] = k k0_dkc[d, k] = Gdir_c else: self.kklst_dk[d, k], k0_dkc[d, k] = neighbor_k_search(k_c, G_c, self.kpt_kc) # Set the inverse list of neighboring k-points self.invkklst_dk = np.empty((self.Ndir, self.Nk), int) for d in range(self.Ndir): for k1 in range(self.Nk): self.invkklst_dk[d, k1] = self.kklst_dk[d].tolist().index(k1) Nw = self.nwannier Nb = self.nbands self.Z_dkww = np.empty((self.Ndir, self.Nk, Nw, Nw), complex) self.V_knw = np.zeros((self.Nk, Nb, Nw), complex) if file is None: self.Z_dknn = np.empty((self.Ndir, self.Nk, Nb, Nb), complex) for d, dirG in enumerate(self.Gdir_dc): for k in range(self.Nk): k1 = self.kklst_dk[d, k] k0_c = k0_dkc[d, k] self.Z_dknn[d, k] = calc.get_wannier_localization_matrix( nbands=Nb, dirG=dirG, kpoint=k, nextkpoint=k1, G_I=k0_c, spin=self.spin ) self.initialize(file=file, initialwannier=initialwannier, seed=seed)
def __init__(self, kpts, nspins=1, collinear=True): """Construct descriptor object for kpoint/spin combinations (ks-pair). Parameters ---------- kpts: None, sequence of 3 ints, or (n,3)-shaped array Specification of the k-point grid. None=Gamma, list of ints=Monkhorst-Pack, ndarray=user specified. nspins: int Number of spins. Attributes =================== ================================================= ``N_c`` Number of k-points in the different directions. ``nspins`` Number of spins in total. ``mynspins`` Number of spins on this CPU. ``nibzkpts`` Number of irreducible kpoints in 1st BZ. ``nks`` Number of k-point/spin combinations in total. ``mynks`` Number of k-point/spin combinations on this CPU. ``gamma`` Boolean indicator for gamma point calculation. ``comm`` MPI-communicator for kpoint distribution. ``weight_k`` Weights of each k-point ``ibzk_kc`` Unknown ``ibzk_qc`` Unknown ``sym_k`` Unknown ``time_reversal_k`` Unknown ``bz2ibz_k`` Unknown ``ibz2bz_k`` Unknown ``bz2bz_ks`` Unknown ``symmetry`` Object representing symmetries =================== ================================================= """ if kpts is None: self.bzk_kc = np.zeros((1, 3)) self.N_c = np.array((1, 1, 1), dtype=int) self.offset_c = np.zeros(3) elif isinstance(kpts[0], int): self.bzk_kc = monkhorst_pack(kpts) self.N_c = np.array(kpts, dtype=int) self.offset_c = np.zeros(3) else: self.bzk_kc = np.array(kpts, float) try: self.N_c, self.offset_c = \ get_monkhorst_pack_size_and_offset(self.bzk_kc) except ValueError: self.N_c = None self.offset_c = None self.collinear = collinear self.nspins = nspins self.nbzkpts = len(self.bzk_kc) # Gamma-point calculation? self.gamma = self.nbzkpts == 1 and np.allclose(self.bzk_kc, 0) # Point group and time-reversal symmetry neglected: self.weight_k = np.ones(self.nbzkpts) / self.nbzkpts self.ibzk_kc = self.bzk_kc.copy() self.sym_k = np.zeros(self.nbzkpts, int) self.time_reversal_k = np.zeros(self.nbzkpts, bool) self.bz2ibz_k = np.arange(self.nbzkpts) self.ibz2bz_k = np.arange(self.nbzkpts) self.bz2bz_ks = np.arange(self.nbzkpts)[:, np.newaxis] self.nibzkpts = self.nbzkpts self.nks = self.nibzkpts * self.nspins self.set_communicator(mpi.serial_comm) if self.gamma: self.description = '1 k-point (Gamma)' else: self.description = '%d k-points' % self.nbzkpts if self.N_c is not None: self.description += (': %d x %d x %d Monkhorst-Pack grid' % tuple(self.N_c)) if self.offset_c.any(): self.description += ' + [' for x in self.offset_c: if x != 0 and abs(round(1 / x) - 1 / x) < 1e-12: self.description += '1/%d,' % round(1 / x) else: self.description += '%f,' % x self.description = self.description[:-1] + ']'
def get_realspace_hs(h_skmm, s_kmm, bzk_kc, weight_k, R_c=(0, 0, 0), direction='x', symmetry={'enabled': False}): from gpaw.symmetry import Symmetry from ase.dft.kpoints import get_monkhorst_pack_size_and_offset, \ monkhorst_pack if symmetry['point_group'] is True: raise NotImplementedError, 'Point group symmetry not implemented' nspins, nk, nbf = h_skmm.shape[:3] dir = 'xyz'.index(direction) transverse_dirs = np.delete([0, 1, 2], [dir]) dtype = float if len(bzk_kc) > 1 or np.any(bzk_kc[0] != [0, 0, 0]): dtype = complex kpts_grid = get_monkhorst_pack_size_and_offset(bzk_kc)[0] # kpts in the transport direction nkpts_p = kpts_grid[dir] bzk_p_kc = monkhorst_pack((nkpts_p,1,1))[:, 0] weight_p_k = 1. / nkpts_p # kpts in the transverse directions bzk_t_kc = monkhorst_pack(tuple(kpts_grid[transverse_dirs]) + (1, )) if not 'time_reversal' in symmetry: symmetry['time_reversal'] = True if symmetry['time_reversal'] is True: #XXX a somewhat ugly hack: # By default GPAW reduces inversion sym in the z direction. The steps # below assure reduction in the transverse dirs. # For now this part seems to do the job, but it may be written # in a smarter way in the future. symmetry = Symmetry([1], np.eye(3)) symmetry.prune_symmetries_atoms(np.zeros((1, 3))) ibzk_kc, ibzweight_k = symmetry.reduce(bzk_kc)[:2] ibzk_t_kc, weights_t_k = symmetry.reduce(bzk_t_kc)[:2] ibzk_t_kc = ibzk_t_kc[:, :2] nkpts_t = len(ibzk_t_kc) else: ibzk_kc = bzk_kc.copy() ibzk_t_kc = bzk_t_kc nkpts_t = len(bzk_t_kc) weights_t_k = [1. / nkpts_t for k in range(nkpts_t)] h_skii = np.zeros((nspins, nkpts_t, nbf, nbf), dtype) if s_kmm is not None: s_kii = np.zeros((nkpts_t, nbf, nbf), dtype) tol = 7 for j, k_t in enumerate(ibzk_t_kc): for k_p in bzk_p_kc: k = np.zeros((3,)) k[dir] = k_p k[transverse_dirs] = k_t kpoint_list = [list(np.round(k_kc, tol)) for k_kc in ibzk_kc] if list(np.round(k, tol)) not in kpoint_list: k = -k # inversion index = kpoint_list.index(list(np.round(k,tol))) h = h_skmm[:, index].conjugate() if s_kmm is not None: s = s_kmm[index].conjugate() k=-k else: # kpoint in the ibz index = kpoint_list.index(list(np.round(k, tol))) h = h_skmm[:, index] if s_kmm is not None: s = s_kmm[index] c_k = np.exp(2.j * np.pi * np.dot(k, R_c)) * weight_p_k h_skii[:, j] += c_k * h if s_kmm is not None: s_kii[j] += c_k * s if s_kmm is None: return ibzk_t_kc, weights_t_k, h_skii else: return ibzk_t_kc, weights_t_k, h_skii, s_kii
def get_berry_phases(calc, spin=0, dir=0, check2d=False): if isinstance(calc, str): calc = GPAW(calc, communicator=serial_comm, txt=None) M = np.round(calc.get_magnetic_moment()) assert np.allclose(M, calc.get_magnetic_moment(), atol=0.05), \ print(M, calc.get_magnetic_moment()) nvalence = calc.wfs.setups.nvalence nocc_s = [int((nvalence + M) / 2), int((nvalence - M) / 2)] assert np.allclose(np.sum(nocc_s), nvalence) nocc = nocc_s[spin] bands = list(range(nocc)) kpts_kc = calc.get_bz_k_points() size = get_monkhorst_pack_size_and_offset(kpts_kc)[0] Nk = len(kpts_kc) wfs = calc.wfs icell_cv = (2 * np.pi) * np.linalg.inv(calc.wfs.gd.cell_cv).T dO_aii = [] for ia, id in enumerate(wfs.setups.id_a): dO_ii = calc.wfs.setups[ia].dO_ii dO_aii.append(dO_ii) kd = calc.wfs.kd nik = kd.nibzkpts u_knR = [] P_kani = [] for k in range(Nk): ik = kd.bz2ibz_k[k] k_c = kd.bzk_kc[k] ik_c = kd.ibzk_kc[ik] kpt = wfs.kpt_u[ik + spin * nik] psit_nG = kpt.psit_nG ut_nR = wfs.gd.empty(nocc, wfs.dtype) # Check that all states are occupied assert np.all(kpt.f_n[:nocc] > 1e-6) sym = kd.sym_k[k] U_cc = kd.symmetry.op_scc[sym] time_reversal = kd.time_reversal_k[k] sign = 1 - 2 * time_reversal phase_c = k_c - sign * np.dot(U_cc, ik_c) phase_c = phase_c.round().astype(int) N_c = wfs.gd.N_c if (U_cc == np.eye(3)).all() or np.allclose(ik_c - k_c, 0.0): for n in range(nocc): ut_nR[n, :] = wfs.pd.ifft(psit_nG[n], ik) else: i_cr = np.dot(U_cc.T, np.indices(N_c).reshape((3, -1))) i = np.ravel_multi_index(i_cr, N_c, 'wrap') for n in range(nocc): ut_nR[n, :] = wfs.pd.ifft(psit_nG[n], ik).ravel()[i].reshape(N_c) if time_reversal: ut_nR = ut_nR.conj() if np.any(phase_c): emikr_R = np.exp(-2j * np.pi * np.dot(np.indices(N_c).T, phase_c / N_c).T) u_knR.append(ut_nR * emikr_R[np.newaxis]) else: u_knR.append(ut_nR) a_a = [] U_aii = [] for a, id in enumerate(wfs.setups.id_a): b = kd.symmetry.a_sa[sym, a] S_c = np.dot(calc.spos_ac[a], U_cc) - calc.spos_ac[b] x = np.exp(2j * np.pi * np.dot(k_c, S_c)) U_ii = wfs.setups[a].R_sii[sym].T * x a_a.append(b) U_aii.append(U_ii) P_ani = [] for b, U_ii in zip(a_a, U_aii): P_ni = np.dot(kpt.P_ani[b][:nocc], U_ii) if time_reversal: P_ni = P_ni.conj() P_ani.append(P_ni) P_kani.append(P_ani) indices_kkk = np.arange(Nk).reshape(size) tmp = np.concatenate([[i for i in range(3) if i != dir], [dir]]) indices_kk = indices_kkk.transpose(tmp).reshape(-1, size[dir]) nkperp = len(indices_kk) phases = [] if check2d: phases2d = [] for indices_k in indices_kk: M_knn = [] for j in range(size[dir]): k1 = indices_k[j] G_c = np.array([0, 0, 0]) if j + 1 < size[dir]: k2 = indices_k[j + 1] else: k2 = indices_k[0] G_c[dir] = 1 u1_nR = u_knR[k1] u2_nR = u_knR[k2] k1_c = kpts_kc[k1] k2_c = kpts_kc[k2] + G_c if np.any(G_c): emiGr_R = np.exp(-2j * np.pi * np.dot(np.indices(N_c).T, G_c / N_c).T) u2_nR = u2_nR * emiGr_R bG_c = k2_c - k1_c bG_v = np.dot(bG_c, icell_cv) M_nn = get_overlap(calc, bands, np.reshape(u1_nR, (nocc, -1)), np.reshape(u2_nR, (nocc, -1)), P_kani[k1], P_kani[k2], dO_aii, bG_v) M_knn.append(M_nn) det = np.linalg.det(M_knn) phases.append(np.imag(np.log(np.prod(det)))) if check2d: # In the case of 2D systems we can check the # result k1 = indices_k[0] k1_c = kpts_kc[k1] G_c = [0, 0, 1] G_v = np.dot(G_c, icell_cv) u1_nR = u_knR[k1] emiGr_R = np.exp(-2j * np.pi * np.dot(np.indices(N_c).T, G_c / N_c).T) u2_nR = u1_nR * emiGr_R M_nn = get_overlap(calc, bands, np.reshape(u1_nR, (nocc, -1)), np.reshape(u2_nR, (nocc, -1)), P_kani[k1], P_kani[k1], dO_aii, G_v) phase2d = np.imag(np.log(np.linalg.det(M_nn))) phases2d.append(phase2d) # Make sure the phases are continuous for p in range(nkperp - 1): delta = phases[p] - phases[p + 1] phases[p + 1] += np.round(delta / (2 * np.pi)) * 2 * np.pi phase = np.sum(phases) / nkperp if check2d: for p in range(nkperp - 1): delta = phases2d[p] - phases2d[p + 1] phases2d[p + 1] += np.round(delta / (2 * np.pi)) * 2 * np.pi phase2d = np.sum(phases2d) / nkperp diff = abs(phase - phase2d) if diff > 0.01: msg = 'Warning wrong phase: phase={}, 2dphase={}' print(msg.format(phase, phase2d)) return indices_kk, phases
def create_kpoint_descriptor_with_refinement(refine, bzkpts_kc, nspins, atoms, symmetry, comm, timer): """Main routine to build refined k-point grids.""" if 'center' not in refine: raise RuntimeError('Center for refinement not given!') if 'size' not in refine: raise RuntimeError('Grid size for refinement not given!') center_ic = np.array(refine.get('center'), dtype=float, ndmin=2) size = np.array(refine.get('size'), ndmin=2) reduce_symmetry = refine.get('reduce_symmetry', True) # Check that all sizes are odd. That's not so much an issue really. # But even Monkhorst-Pack grids have points on the boundary, # which just would require more special casing, which I want to avoid. if (np.array(size) % 2 == 0).any(): raise RuntimeError( 'Grid size for refinement must be odd! Is: {}'.format(size)) # Arguments needed for k-point descriptor construction kwargs = { 'nspins': nspins, 'atoms': atoms, 'symmetry': symmetry, 'comm': comm } # Define coarse grid points bzk_coarse_kc = bzkpts_kc # Define fine grid points centers_i, bzk_fine_kc, weight_fine_k = get_fine_bzkpts( center_ic, size, bzk_coarse_kc, kwargs) if reduce_symmetry: # Define new symmetry object ignoring symmetries violated by the # refined kpoints kd_fine = create_kpoint_descriptor(bzk_fine_kc, **kwargs) symm = prune_symmetries_kpoints(kd_fine, symmetry) del kd_fine else: symm = copy.copy(symmetry) kwargs['symmetry'] = symm # Create new descriptor with both sets of points with timer('Create mixed descriptor'): kd = create_mixed_kpoint_descriptor(bzk_coarse_kc, bzk_fine_kc, centers_i, weight_fine_k, kwargs) # Add missing k-points to fulfill group properties with # zero-weighted points with timer('Add_missing_points'): kd = add_missing_points(kd, kwargs) # Add additional +q k-points, if necessary if 'q' in refine: timer.start("+q") N_coarse_c = get_monkhorst_pack_size_and_offset(bzk_coarse_kc)[0] bla = N_coarse_c * refine['q'] if not max(abs(bla - np.rint(bla))) < 1e-8: kd.refine_info.almostoptical = True kd = add_plusq_points(kd, refine['q'], kwargs) symm = kd.symmetry kwargs['symmetry'] = symm kd = add_missing_points(kd, kwargs) timer.stop("+q") return kd
def __init__( self, evals, wfn, positions, kpts, nwann, weight_func, Sk=None, has_phase=True, Rgrid=None, exclude_bands=None, ): #eigen self.evals = np.array(evals, dtype=float) self.kpts = np.array(kpts, dtype=float) # TODO: Remove me.modify evals if False: shift = 0.3 shift2 = shift - 0.01 ik = self.find_k((0.5, 0, 0.5)) print(ik) self.evals[ik, 0] -= shift self.evals[ik, 1] -= shift2 ik = self.find_k((0.5, 0, -0.5)) print(ik) self.evals[ik, 0] -= shift self.evals[ik, 1] -= shift2 ik = self.find_k((-0.5, 0, -0.5)) print(ik) self.evals[ik, 0] -= shift self.evals[ik, 1] -= shift2 ik = self.find_k((-0.5, 0, 0.5)) print(ik) self.evals[ik, 0] -= shift self.evals[ik, 1] -= shift2 self.ndim = self.kpts.shape[1] self.nkpt, self.nbasis, self.nband = np.shape(wfn) if Sk is None: self.is_orthogonal = True else: self.S = Sk self.is_orthogonal = False # exclude bands if exclude_bands is None: exclude_bands = [] self.ibands = tuple( [i for i in range(self.nband) if i not in exclude_bands]) self.nband = len(self.ibands) self.nwann = nwann self.positions = positions # kpts self.nkpt = self.kpts.shape[0] self.kmesh, self.k_offset = get_monkhorst_pack_size_and_offset( self.kpts) self.kweight = np.ones(self.nkpt, dtype=float) / self.nkpt self.weight_func = weight_func # Rgrid self.Rgrid = Rgrid self._prepare_Rlist() self.nR = self.Rlist.shape[0] # calculate occupation functions self.occ = self.weight_func(self.evals[:, self.ibands]) # remove e^ikr from wfn self.has_phase = has_phase if not has_phase: self.psi = wfn else: self._remove_phase(wfn) self.Amn = np.zeros((self.nkpt, self.nband, self.nwann), dtype=complex) self.wannk = np.zeros((self.nkpt, self.nbasis, self.nwann), dtype=complex) self.Hwann_k = np.zeros((self.nkpt, self.nwann, self.nwann), dtype=complex) self.HwannR = np.zeros((self.nR, self.nwann, self.nwann), dtype=complex) self.wannR = np.zeros((self.nR, self.nbasis, self.nwann), dtype=complex)
def __init__(self, kpts, nspins=1, collinear=True, usefractrans=False): """Construct descriptor object for kpoint/spin combinations (ks-pair). Parameters ---------- kpts: None, sequence of 3 ints, or (n,3)-shaped array Specification of the k-point grid. None=Gamma, list of ints=Monkhorst-Pack, ndarray=user specified. nspins: int Number of spins. usefractrans: bool Switch for the use of non-symmorphic symmetries aka: symmetries with fractional translations. False by default (experimental!!!) Attributes =================== ================================================= ``N_c`` Number of k-points in the different directions. ``nspins`` Number of spins in total. ``mynspins`` Number of spins on this CPU. ``nibzkpts`` Number of irreducible kpoints in 1st BZ. ``nks`` Number of k-point/spin combinations in total. ``mynks`` Number of k-point/spin combinations on this CPU. ``gamma`` Boolean indicator for gamma point calculation. ``comm`` MPI-communicator for kpoint distribution. ``weight_k`` Weights of each k-point ``ibzk_kc`` Unknown ``sym_k`` Unknown ``time_reversal_k`` Unknown ``bz2ibz_k`` Unknown ``ibz2bz_k`` Unknown ``bz2bz_ks`` Unknown ``symmetry`` Object representing symmetries =================== ================================================= """ if kpts is None: self.bzk_kc = np.zeros((1, 3)) self.N_c = np.array((1, 1, 1), dtype=int) self.offset_c = np.zeros(3) elif isinstance(kpts[0], int): self.bzk_kc = monkhorst_pack(kpts) self.N_c = np.array(kpts, dtype=int) self.offset_c = np.zeros(3) else: self.bzk_kc = np.array(kpts, float) try: self.N_c, self.offset_c = \ get_monkhorst_pack_size_and_offset(self.bzk_kc) except ValueError: self.N_c = None self.offset_c = None self.collinear = collinear self.nspins = nspins self.nbzkpts = len(self.bzk_kc) # Gamma-point calculation? self.usefractrans = usefractrans self.gamma = (self.nbzkpts == 1 and np.allclose(self.bzk_kc[0], 0.0)) self.set_symmetry(None, None, usesymm=None) self.set_communicator(mpi.serial_comm) if self.gamma: self.description = '1 k-point (Gamma)' else: self.description = '%d k-points' % self.nbzkpts if self.N_c is not None: self.description += (': %d x %d x %d Monkhorst-Pack grid' % tuple(self.N_c)) if self.offset_c.any(): self.description += ' + [' for x in self.offset_c: if x != 0 and abs(round(1 / x) - 1 / x) < 1e-12: self.description += '1/%d,' % round(1 / x) else: self.description += '%f,' % x self.description = self.description[:-1] + ']'
def __init__(self, kpts, nspins=1): """Construct descriptor object for kpoint/spin combinations (ks-pair). Parameters ---------- kpts: None, sequence of 3 ints, or (n,3)-shaped array Specification of the k-point grid. None=Gamma, list of ints=Monkhorst-Pack, ndarray=user specified. nspins: int Number of spins. Attributes =================== ================================================= ``N_c`` Number of k-points in the different directions. ``nspins`` Number of spins in total. ``mynspins`` Number of spins on this CPU. ``nibzkpts`` Number of irreducible kpoints in 1st BZ. ``nks`` Number of k-point/spin combinations in total. ``mynks`` Number of k-point/spin combinations on this CPU. ``gamma`` Boolean indicator for gamma point calculation. ``comm`` MPI-communicator for kpoint distribution. ``weight_k`` Weights of each k-point ``ibzk_kc`` Unknown ``ibzk_qc`` Unknown ``sym_k`` Unknown ``time_reversal_k`` Unknown ``bz2ibz_k`` Unknown ``ibz2bz_k`` Unknown ``bz2bz_ks`` Unknown ``symmetry`` Object representing symmetries =================== ================================================= """ if kpts is None: self.bzk_kc = np.zeros((1, 3)) self.N_c = np.array((1, 1, 1), dtype=int) self.offset_c = np.zeros(3) else: kpts = np.asarray(kpts) if kpts.ndim == 1: self.N_c = np.array(kpts, dtype=int) self.bzk_kc = monkhorst_pack(self.N_c) self.offset_c = np.zeros(3) else: self.bzk_kc = np.array(kpts, dtype=float) try: self.N_c, self.offset_c = \ get_monkhorst_pack_size_and_offset(self.bzk_kc) except ValueError: self.N_c = None self.offset_c = None self.nspins = nspins self.nbzkpts = len(self.bzk_kc) # Gamma-point calculation? self.gamma = self.nbzkpts == 1 and np.allclose(self.bzk_kc, 0) # Point group and time-reversal symmetry neglected: self.weight_k = np.ones(self.nbzkpts) / self.nbzkpts self.ibzk_kc = self.bzk_kc.copy() self.sym_k = np.zeros(self.nbzkpts, int) self.time_reversal_k = np.zeros(self.nbzkpts, bool) self.bz2ibz_k = np.arange(self.nbzkpts) self.ibz2bz_k = np.arange(self.nbzkpts) self.bz2bz_ks = np.arange(self.nbzkpts)[:, np.newaxis] self.nibzkpts = self.nbzkpts self.nks = self.nibzkpts * self.nspins self.set_communicator(mpi.serial_comm)
def plot_reciprocal_cell(atoms, path='default', k_points=False, ibz_k_points=False, plot_vectors=True, dimension=3, output=None, verbose=False): import matplotlib.pyplot as plt cell = atoms.get_cell() icell = atoms.get_reciprocal_cell() try: cs = crystal_structure_from_cell(cell) except ValueError: cs = None if verbose: if cs: print('Crystal:', cs) print('Special points:', special_paths[cs]) print('Lattice vectors:') for i, v in enumerate(cell): print('{}: ({:16.9f},{:16.9f},{:16.9f})'.format(i + 1, *v)) print('Reciprocal vectors:') for i, v in enumerate(icell): print('{}: ({:16.9f},{:16.9f},{:16.9f})'.format(i + 1, *v)) # band path if path: if path == 'default': path = special_paths[cs] paths = [] special_points = get_special_points(cell) for names in parse_path_string(path): points = [] for name in names: points.append(np.dot(icell.T, special_points[name])) paths.append((names, points)) else: paths = None # k points points = None if atoms.calc is not None and hasattr(atoms.calc, 'get_bz_k_points'): bzk = atoms.calc.get_bz_k_points() if path is None: try: size, offset = get_monkhorst_pack_size_and_offset(bzk) except ValueError: # This was not a MP-grid. Must be a path in the BZ: path = ''.join(labels_from_kpts(bzk, cell)[2]) if k_points: points = bzk elif ibz_k_points: points = atoms.calc.get_ibz_k_points() if points is not None: for i in range(len(points)): points[i] = np.dot(icell.T, points[i]) kwargs = { 'cell': cell, 'vectors': plot_vectors, 'paths': paths, 'points': points } if dimension == 1: bz1d_plot(**kwargs) elif dimension == 2: bz2d_plot(**kwargs) else: bz3d_plot(interactive=True, **kwargs) if output: plt.savefig(output) else: plt.show()
def __init__(self, nwannier, calc, file=None, nbands=None, fixedenergy=None, fixedstates=None, spin=0, initialwannier='random', rng=np.random, verbose=False): """ Required arguments: ``nwannier``: The number of Wannier functions you wish to construct. This must be at least half the number of electrons in the system and at most equal to the number of bands in the calculation. ``calc``: A converged DFT calculator class. If ``file`` arg. is not provided, the calculator *must* provide the method ``get_wannier_localization_matrix``, and contain the wavefunctions (save files with only the density is not enough). If the localization matrix is read from file, this is not needed, unless ``get_function`` or ``write_cube`` is called. Optional arguments: ``nbands``: Bands to include in localization. The number of bands considered by Wannier can be smaller than the number of bands in the calculator. This is useful if the highest bands of the DFT calculation are not well converged. ``spin``: The spin channel to be considered. The Wannier code treats each spin channel independently. ``fixedenergy`` / ``fixedstates``: Fixed part of Heilbert space. Determine the fixed part of Hilbert space by either a maximal energy *or* a number of bands (possibly a list for multiple k-points). Default is None meaning that the number of fixed states is equated to ``nwannier``. ``file``: Read localization and rotation matrices from this file. ``initialwannier``: Initial guess for Wannier rotation matrix. Can be 'bloch' to start from the Bloch states, 'random' to be randomized, or a list passed to calc.get_initial_wannier. ``rng``: Random number generator for ``initialwannier``. ``verbose``: True / False level of verbosity. """ # Bloch phase sign convention. # May require special cases depending on which code is used. sign = -1 self.nwannier = nwannier self.calc = calc self.spin = spin self.verbose = verbose self.kpt_kc = calc.get_bz_k_points() assert len(calc.get_ibz_k_points()) == len(self.kpt_kc) self.kptgrid = get_monkhorst_pack_size_and_offset(self.kpt_kc)[0] self.kpt_kc *= sign self.Nk = len(self.kpt_kc) self.unitcell_cc = calc.get_atoms().get_cell() self.largeunitcell_cc = (self.unitcell_cc.T * self.kptgrid).T self.weight_d, self.Gdir_dc = calculate_weights(self.largeunitcell_cc) self.Ndir = len(self.weight_d) # Number of directions if nbands is not None: self.nbands = nbands else: self.nbands = calc.get_number_of_bands() if fixedenergy is None: if fixedstates is None: self.fixedstates_k = np.array([nwannier] * self.Nk, int) else: if isinstance(fixedstates, int): fixedstates = [fixedstates] * self.Nk self.fixedstates_k = np.array(fixedstates, int) else: # Setting number of fixed states and EDF from specified energy. # All states below this energy (relative to Fermi level) are fixed. fixedenergy += calc.get_fermi_level() print(fixedenergy) self.fixedstates_k = np.array( [calc.get_eigenvalues(k, spin).searchsorted(fixedenergy) for k in range(self.Nk)], int) self.edf_k = self.nwannier - self.fixedstates_k if verbose: print('Wannier: Fixed states : %s' % self.fixedstates_k) print('Wannier: Extra degrees of freedom: %s' % self.edf_k) # Set the list of neighboring k-points k1, and the "wrapping" k0, # such that k1 - k - G + k0 = 0 # # Example: kpoints = (-0.375,-0.125,0.125,0.375), dir=0 # G = [0.25,0,0] # k=0.375, k1= -0.375 : -0.375-0.375-0.25 => k0=[1,0,0] # # For a gamma point calculation k1 = k = 0, k0 = [1,0,0] for dir=0 if self.Nk == 1: self.kklst_dk = np.zeros((self.Ndir, 1), int) k0_dkc = self.Gdir_dc.reshape(-1, 1, 3) else: self.kklst_dk = np.empty((self.Ndir, self.Nk), int) k0_dkc = np.empty((self.Ndir, self.Nk, 3), int) # Distance between kpoints kdist_c = np.empty(3) for c in range(3): # make a sorted list of the kpoint values in this direction slist = np.argsort(self.kpt_kc[:, c], kind='mergesort') skpoints_kc = np.take(self.kpt_kc, slist, axis=0) kdist_c[c] = max([skpoints_kc[n + 1, c] - skpoints_kc[n, c] for n in range(self.Nk - 1)]) for d, Gdir_c in enumerate(self.Gdir_dc): for k, k_c in enumerate(self.kpt_kc): # setup dist vector to next kpoint G_c = np.where(Gdir_c > 0, kdist_c, 0) if max(G_c) < 1e-4: self.kklst_dk[d, k] = k k0_dkc[d, k] = Gdir_c else: self.kklst_dk[d, k], k0_dkc[d, k] = \ neighbor_k_search(k_c, G_c, self.kpt_kc) # Set the inverse list of neighboring k-points self.invkklst_dk = np.empty((self.Ndir, self.Nk), int) for d in range(self.Ndir): for k1 in range(self.Nk): self.invkklst_dk[d, k1] = self.kklst_dk[d].tolist().index(k1) Nw = self.nwannier Nb = self.nbands self.Z_dkww = np.empty((self.Ndir, self.Nk, Nw, Nw), complex) self.V_knw = np.zeros((self.Nk, Nb, Nw), complex) if file is None: self.Z_dknn = np.empty((self.Ndir, self.Nk, Nb, Nb), complex) for d, dirG in enumerate(self.Gdir_dc): for k in range(self.Nk): k1 = self.kklst_dk[d, k] k0_c = k0_dkc[d, k] self.Z_dknn[d, k] = calc.get_wannier_localization_matrix( nbands=Nb, dirG=dirG, kpoint=k, nextkpoint=k1, G_I=k0_c, spin=self.spin) self.initialize(file=file, initialwannier=initialwannier, rng=rng)
def __init__(self, calc=None, width=0.1, window=None, npts=401, comm=world, nspins=None, w_k=None, e_skn=None): """Electronic Density Of States object. calc: calculator object Any ASE compliant calculator object. width: float Width of guassian smearing. Use width=0.0 for linear tetrahedron interpolation. window: tuple of two float Use ``window=(emin, emax)``. If not specified, a window big enough to hold all the eigenvalues will be used. npts: int Number of points. comm: communicator object MPI communicator for lti_dos nspins: int number of spin channels w_k: list k-point weights e_skn: numpy array Kohn-Sham eigenvalues indexed by spin & k-point """ self.comm = comm self.npts = npts self.width = width if calc is None: for kw in 'nspins', 'w_k', 'e_skn': val = locals()[kw] if val is not None: setattr(self, kw, val) else: raise ValueError( f'When initialising a DOS object with calc=None, you must specify a value for {kw}' ) else: self.w_k = calc.get_k_point_weights() self.nspins = calc.get_number_of_spins() self.e_skn = np.array([[ calc.get_eigenvalues(kpt=k, spin=s) for k in range(len(self.w_k)) ] for s in range(self.nspins)]) try: # two Fermi levels for i, eF in enumerate(calc.get_fermi_level()): self.e_skn[i] -= eF except TypeError: # a single Fermi level self.e_skn -= calc.get_fermi_level() if window is None: emin = None emax = None else: emin, emax = window if emin is None: emin = self.e_skn.min() - 5 * self.width if emax is None: emax = self.e_skn.max() + 5 * self.width self.energies = np.linspace(emin, emax, npts) if width == 0.0: if calc is None: raise NotImplementedError( f'When initialising a DOS object with width=0.0, you must specify a value for calc' ) bzkpts = calc.get_bz_k_points() size, offset = get_monkhorst_pack_size_and_offset(bzkpts) bz2ibz = calc.get_bz_to_ibz_map() shape = (self.nspins, ) + tuple(size) + (-1, ) self.e_skn = self.e_skn[:, bz2ibz].reshape(shape) self.cell = calc.atoms.cell