def get_dos(self, kpts=(10, 10, 10), npts=1000, delta=1e-3, indices=None): from ase.spectrum.dosdata import RawDOSData # dos = self.dos(kpts, npts, delta, indices) kpts_kc = monkhorst_pack(kpts) omega_w = self.band_structure(kpts_kc).ravel() dos = RawDOSData(omega_w, np.ones_like(omega_w)) return dos
def get_dos(self, kpts=(10, 10, 10), npts=1000, delta=1e-3, indices=None): #dos = self.dos(kpts, npts, delta, indices) kpts_kc = monkhorst_pack(kpts) omega_w = self.band_structure(kpts_kc).ravel() from ase.dft.pdos import DOS dos = DOS(omega_w, np.ones_like(omega_w)[None]) return dos
def dos(self, kpts=(10, 10, 10), npts=1000, delta=1e-3, indices=None): """Calculate phonon dos as a function of energy. Parameters ---------- qpts: tuple Shape of Monkhorst-Pack grid for sampling the Brillouin zone. npts: int Number of energy points. delta: float Broadening of Lorentzian line-shape in eV. indices: list If indices is not None, the atomic-partial dos for the specified atoms will be calculated. """ # Monkhorst-Pack grid kpts_kc = monkhorst_pack(kpts) N = np.prod(kpts) # Get frequencies omega_kl = self.band_structure(kpts_kc) # Energy axis and dos omega_e = np.linspace(0., np.amax(omega_kl) + 5e-3, num=npts) dos_e = np.zeros_like(omega_e) # Sum up contribution from all q-points and branches for omega_l in omega_kl: diff_el = (omega_e[:, np.newaxis] - omega_l[np.newaxis, :])**2 dos_el = 1. / (diff_el + (0.5 * delta)**2) dos_e += dos_el.sum(axis=1) dos_e *= 1. / (N * pi) * 0.5 * delta return omega_e, dos_e
def kpts2ndarray(kpts): """Convert kpts keyword to 2d ndarray of scaled k-points.""" if kpts is None: return np.zeros((1, 3)) if isinstance(kpts[0], int): return monkhorst_pack(kpts) return np.array(kpts)
def dos(self, kpts=(10, 10, 10), npts=1000, delta=1e-3, indices=None): """Calculate phonon dos as a function of energy. Parameters: qpts: tuple Shape of Monkhorst-Pack grid for sampling the Brillouin zone. npts: int Number of energy points. delta: float Broadening of Lorentzian line-shape in eV. indices: list If indices is not None, the atomic-partial dos for the specified atoms will be calculated. """ # Monkhorst-Pack grid kpts_kc = monkhorst_pack(kpts) N = np.prod(kpts) # Get frequencies omega_kl = self.band_structure(kpts_kc) # Energy axis and dos omega_e = np.linspace(0., np.amax(omega_kl) + 5e-3, num=npts) dos_e = np.zeros_like(omega_e) # Sum up contribution from all q-points and branches for omega_l in omega_kl: diff_el = (omega_e[:, np.newaxis] - omega_l[np.newaxis, :])**2 dos_el = 1. / (diff_el + (0.5 * delta)**2) dos_e += dos_el.sum(axis=1) dos_e *= 1. / (N * pi) * 0.5 * delta return omega_e, dos_e
def calculate_q(self, chi0, pd, chi0_swGG, chi0_swxvG, chi0_swvv, m1, m2, cut_G, A2_x): chi0_wGG = chi0_swGG[0] if chi0_swxvG is not None: chi0_wxvG = chi0_swxvG[0] chi0_wvv = chi0_swvv[0] else: chi0_wxvG = None chi0_wvv = None chi0._calculate(pd, chi0_wGG, chi0_wxvG, chi0_wvv, m1, m2, spins='all', extend_head=False) print('E_c(q) = ', end='', file=self.fd) chi0_wGG = chi0.redistribute(chi0_wGG, A2_x) if not pd.kd.gamma: e = self.calculate_energy(pd, chi0_wGG, cut_G) print('%.3f eV' % (e * Hartree), file=self.fd) self.fd.flush() else: from ase.dft import monkhorst_pack kd = self.calc.wfs.kd N = 4 N_c = np.array([N, N, N]) if self.truncation is not None: N_c[kd.N_c == 1] = 1 q_qc = monkhorst_pack(N_c) / kd.N_c q_qc *= 1.0e-6 U_scc = kd.symmetry.op_scc q_qc = kd.get_ibz_q_points(q_qc, U_scc)[0] weight_q = kd.q_weights q_qv = 2 * np.pi * np.dot(q_qc, pd.gd.icell_cv) nw = len(self.omega_w) mynw = nw // self.nblocks w1 = self.blockcomm.rank * mynw w2 = w1 + mynw a_qw = np.sum(np.dot(chi0_wvv[w1:w2], q_qv.T) * q_qv.T, axis=1).T a0_qwG = np.dot(q_qv, chi0_wxvG[w1:w2, 0]) a1_qwG = np.dot(q_qv, chi0_wxvG[w1:w2, 1]) e = 0 for iq in range(len(q_qv)): chi0_wGG[:, 0] = a0_qwG[iq] chi0_wGG[:, :, 0] = a1_qwG[iq] chi0_wGG[:, 0, 0] = a_qw[iq] ev = self.calculate_energy(pd, chi0_wGG, cut_G, q_v=q_qv[iq]) e += ev * weight_q[iq] print('%.3f eV' % (e * Hartree), file=self.fd) self.fd.flush() return e
def get_integrated_kernel(pd, N_c, truncation=None, N=100, reduced=False): from scipy.special import j1, k0, j0, k1 B_cv = 2 * np.pi * pd.gd.icell_cv Nf_c = np.array([N, N, N]) if reduced: # Only integrate periodic directions if truncation is used Nf_c[np.where(N_c == 1)[0]] = 1 q_qc = monkhorst_pack(Nf_c) / N_c q_qc += pd.kd.ibzk_kc[0] q_qv = np.dot(q_qc, B_cv) if truncation is None: V_q = 4 * np.pi / np.sum(q_qv**2, axis=1) elif truncation == '2D': # The non-periodic direction is determined from k-point grid Nn_c = np.where(N_c == 1)[0] Np_c = np.where(N_c != 1)[0] if len(Nn_c) != 1: # The k-point grid does not fit with boundary conditions Nn_c = [2] # Choose reduced cell vectors 0, 1 Np_c = [0, 1] # Choose reduced cell vector 2 # Truncation length is half of cell vector in non-periodic direction R = pd.gd.cell_cv[Nn_c[0], Nn_c[0]] / 2. qp_q = ((q_qv[:, Np_c[0]])**2 + (q_qv[:, Np_c[1]]**2))**0.5 qn_q = q_qv[:, Nn_c[0]] V_q = 4 * np.pi / (q_qv**2).sum(axis=1) a_q = qn_q / qp_q * np.sin(qn_q * R) - np.cos(qn_q * R) V_q *= 1. + np.exp(-qp_q * R) * a_q elif truncation == '1D': # The non-periodic direction is determined from k-point grid Nn_c = np.where(N_c == 1)[0] Np_c = np.where(N_c != 1)[0] if len(Nn_c) != 2: # The k-point grid does not fit with boundary conditions Nn_c = [0, 1] # Choose reduced cell vectors 0, 1 Np_c = [2] # Choose reduced cell vector 2 # The radius is determined from area of non-periodic part of cell Acell_cv = pd.gd.cell_cv[Nn_c, :][:, Nn_c] R = (np.linalg.det(Acell_cv) / np.pi)**0.5 qnR_q = (q_qv[:, Nn_c[0]]**2 + q_qv[:, Nn_c[1]]**2)**0.5 * R qpR_q = abs(q_qv[:, Np_c[0]]) * R V_q = 4 * np.pi / (q_qv**2).sum(axis=1) V_q *= (1.0 + qnR_q * j1(qnR_q) * k0(qpR_q) - qpR_q * j0(qnR_q) * k1(qpR_q)) elif truncation == '0D' or 'wigner-seitz': R = (3 * np.linalg.det(pd.gd.cell_cv) / (4 * np.pi))**(1. / 3.) q2_q = (q_qv**2).sum(axis=1) V_q = 4 * np.pi / q2_q V_q *= 1.0 - np.cos(q2_q**0.5 * R) return np.sum(V_q) / len(V_q), np.sum(V_q**0.5) / len(V_q)
def __init__(self, kpts, nspins): """Construct descriptor object for kpoint/spin combinations (ks-pair). Parameters ---------- kpts: None, list of ints, or ndarray 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. ``nibzkpts`` Number of irreducible kpoints in 1st Brillouin zone. ``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. ============ ====================================================== """ if kpts is None: self.bzk_kc = np.zeros((1, 3)) self.N_c = np.array((1, 1, 1), dtype=int) elif isinstance(kpts[0], int): self.bzk_kc = monkhorst_pack(kpts) self.N_c = np.array(kpts, dtype=int) else: self.bzk_kc = np.array(kpts) self.N_c = None self.nspins = nspins self.nbzkpts = len(self.bzk_kc) # Gamma-point calculation self.gamma = self.nbzkpts == 1 and not self.bzk_kc[0].any() self.symmetry = None self.comm = None self.ibzk_kc = None self.weight_k = None self.nibzkpts = None self.rank0 = None self.mynks = None self.ks0 = None self.ibzk_qc = None
def get_phonons(self, kpts=(50, 50, 50), npts=5000): """Calculate the phonon spectrum and DOS. Parameters ---------- kpts : tuple Number of points in each directions of the k-space grid. npts : int Number of energy points to calculate the DOS at. """ self.phonons = Phonons(self.atoms, self.calc(), supercell=self.supercell_size, delta=0.05, name=self.name) self.phonons.run() # Read forces and assemble the dynamical matrix self.phonons.read(acoustic=True) self.phonon_kpts_mp = monkhorst_pack(kpts) self.phonon_energy_mp = self.phonons.band_structure( self.phonon_kpts_mp) self.phonon_energy, self.phonon_dos = \ self.phonons.dos(kpts=kpts, npts=npts, delta=5e-4)
from __future__ import print_function from ase import Atoms from ase.build import bulk from ase.dft import monkhorst_pack from ase.parallel import paropen from gpaw import GPAW, FermiDirac from gpaw.wavefunctions.pw import PW from gpaw.xc.exx import EXX import numpy as np # Monkhorst-Pack grid shifted to be gamma centered k = 8 kpts = monkhorst_pack([k, k, k]) kpts += [1. / (2 * k), 1. / (2 * k), 1. / (2 * k)] cell = bulk('C', 'fcc', a=3.553).get_cell() a = Atoms('C2', cell=cell, pbc=True, scaled_positions=((0, 0, 0), (0.25, 0.25, 0.25))) calc = GPAW(mode=PW(600), xc='PBE', occupations=FermiDirac(width=0.01), convergence={'density': 1.e-6}, kpts=kpts, parallel={'domain': 1}, txt='diamond.ralda_01_pbe.txt') a.set_calculator(calc) E_pbe = a.get_potential_energy()
from ase.dft import monkhorst_pack assert [0, 0, 0] in monkhorst_pack((1, 3, 5)).tolist() assert [0, 0, 0] not in monkhorst_pack((1, 3, 6)).tolist() assert len(monkhorst_pack((3, 4, 6))) == 3 * 4 * 6 from ase.units import * print Hartree, Bohr, kJ/mol, kcal/mol, kB*300, fs, 1/fs
def calculate_screened_potential(self, ac): """Calculate W_GG(q)""" chi0 = Chi0(self.calc, frequencies=[0.0], eta=0.001, ecut=self.ecut, intraband=False, hilbert=False, nbands=self.nbands, txt='chi0.txt', world=world, ) self.blockcomm = chi0.blockcomm wfs = self.calc.wfs self.Q_qaGii = [] self.W_qGG = [] self.pd_q = [] t0 = time() print('Calculating screened potential', file=self.fd) for iq, q_c in enumerate(self.qd.ibzk_kc): thisqd = KPointDescriptor([q_c]) pd = PWDescriptor(self.ecut, wfs.gd, complex, thisqd) nG = pd.ngmax chi0.Ga = self.blockcomm.rank * nG chi0.Gb = min(chi0.Ga + nG, nG) chi0_wGG = np.zeros((1, nG, nG), complex) if np.allclose(q_c, 0.0): chi0_wxvG = np.zeros((1, 2, 3, nG), complex) chi0_wvv = np.zeros((1, 3, 3), complex) else: chi0_wxvG = None chi0_wvv = None chi0._calculate(pd, chi0_wGG, chi0_wxvG, chi0_wvv, 0, self.nbands, spins='all', extend_head=False) chi0_GG = chi0_wGG[0] # Calculate eps^{-1}_GG if pd.kd.gamma: # Generate fine grid in vicinity of gamma kd = self.calc.wfs.kd N = 4 N_c = np.array([N, N, N]) if self.truncation is not None: # Only average periodic directions if trunction is used N_c[kd.N_c == 1] = 1 qf_qc = monkhorst_pack(N_c) / kd.N_c qf_qc *= 1.0e-6 U_scc = kd.symmetry.op_scc qf_qc = kd.get_ibz_q_points(qf_qc, U_scc)[0] weight_q = kd.q_weights qf_qv = 2 * np.pi * np.dot(qf_qc, pd.gd.icell_cv) a_q = np.sum(np.dot(chi0_wvv[0], qf_qv.T) * qf_qv.T, axis=0) a0_qG = np.dot(qf_qv, chi0_wxvG[0, 0]) a1_qG = np.dot(qf_qv, chi0_wxvG[0, 1]) einv_GG = np.zeros((nG, nG), complex) # W_GG = np.zeros((nG, nG), complex) for iqf in range(len(qf_qv)): chi0_GG[0] = a0_qG[iqf] chi0_GG[:, 0] = a1_qG[iqf] chi0_GG[0, 0] = a_q[iqf] sqrV_G = get_coulomb_kernel(pd, kd.N_c, truncation=self.truncation, wstc=self.wstc, q_v=qf_qv[iqf])**0.5 sqrV_G *= ac**0.5 # Multiply by adiabatic coupling e_GG = np.eye(nG) - chi0_GG * sqrV_G * sqrV_G[:, np.newaxis] einv_GG += np.linalg.inv(e_GG) * weight_q[iqf] # einv_GG = np.linalg.inv(e_GG) * weight_q[iqf] # W_GG += (einv_GG * sqrV_G * sqrV_G[:, np.newaxis] # * weight_q[iqf]) else: sqrV_G = get_coulomb_kernel(pd, self.kd.N_c, truncation=self.truncation, wstc=self.wstc)**0.5 sqrV_G *= ac**0.5 # Multiply by adiabatic coupling e_GG = np.eye(nG) - chi0_GG * sqrV_G * sqrV_G[:, np.newaxis] einv_GG = np.linalg.inv(e_GG) # W_GG = einv_GG * sqrV_G * sqrV_G[:, np.newaxis] # Now calculate W_GG if pd.kd.gamma: # Reset bare Coulomb interaction sqrV_G = get_coulomb_kernel(pd, self.kd.N_c, truncation=self.truncation, wstc=self.wstc)**0.5 W_GG = einv_GG * sqrV_G * sqrV_G[:, np.newaxis] if self.integrate_gamma != 0: # Numerical integration of Coulomb interaction at all q-points if self.integrate_gamma == 2: reduced = True else: reduced = False V0, sqrV0 = get_integrated_kernel(pd, self.kd.N_c, truncation=self.truncation, reduced=reduced, N=100) W_GG[0, 0] = einv_GG[0, 0] * V0 W_GG[0, 1:] = einv_GG[0, 1:] * sqrV0 * sqrV_G[1:] W_GG[1:, 0] = einv_GG[1:, 0] * sqrV_G[1:] * sqrV0 elif self.integrate_gamma == 0 and pd.kd.gamma: # Analytical integration at gamma bzvol = (2 * np.pi)**3 / self.vol / self.qd.nbzkpts Rq0 = (3 * bzvol / (4 * np.pi))**(1. / 3.) V0 = 16 * np.pi**2 * Rq0 / bzvol sqrV0 = (4 * np.pi)**(1.5) * Rq0**2 / bzvol / 2 W_GG[0, 0] = einv_GG[0, 0] * V0 W_GG[0, 1:] = einv_GG[0, 1:] * sqrV0 * sqrV_G[1:] W_GG[1:, 0] = einv_GG[1:, 0] * sqrV_G[1:] * sqrV0 else: pass if pd.kd.gamma: e = 1 / einv_GG[0, 0].real print(' RPA dielectric constant is: %3.3f' % e, file=self.fd) self.Q_qaGii.append(chi0.Q_aGii) self.pd_q.append(pd) self.W_qGG.append(W_GG) if iq % (self.qd.nibzkpts // 5 + 1) == 2: dt = time() - t0 tleft = dt * self.qd.nibzkpts / (iq + 1) - dt print(' Finished %s q-points in %s - Estimated %s left' % (iq + 1, timedelta(seconds=round(dt)), timedelta(seconds=round(tleft))), file=self.fd)
def __init__(self, calc=None, spinors=False, ecut=10., scale=1.0, nbands=None, valence_bands=None, conduction_bands=None, eshift=None, gw_skn=None, truncation=None, integrate_gamma=1, txt=sys.stdout, mode='BSE', wfile=None, write_h=False, write_v=False): """Creates the BSE object calc: str or calculator object The string should refer to the .gpw file contaning KS orbitals ecut: float Plane wave cutoff energy (eV) nbands: int Number of bands used for the screened interaction valence_bands: list Valence bands used in the BSE Hamiltonian conduction_bands: list Conduction bands used in the BSE Hamiltonian eshift: float Scissors operator opening the gap (eV) gw_skn: list / array List or array defining the gw quasiparticle energies used in the BSE Hamiltonian. Should match spin, k-points and valence/conduction bands truncation: str Coulomb truncation scheme. Can be either wigner-seitz, 2D, 1D, or 0D integrate_gamma: int Method to integrate the Coulomb interaction. 1 is a numerical integration at all q-points with G=[0,0,0] - this breaks the symmetry slightly. 0 is analytical integration at q=[0,0,0] only - this conserves the symmetry. integrate_gamma=2 is the same as 1, but the average is only carried out in the non-periodic directions. txt: str txt output mode: str Theory level used. can be RPA TDHF or BSE. Only BSE is screened. wfile: str File for saving screened interaction and some other stuff needed later write_h: bool If True, write the BSE Hamiltonian to H_SS.ulm. write_v: bool If True, write eigenvalues and eigenstates to v_TS.ulm """ # Calculator if isinstance(calc, str): calc = GPAW(calc, txt=None, communicator=serial_comm) self.calc = calc self.spinors = spinors self.scale = scale assert mode in ['RPA', 'TDHF', 'BSE'] # assert calc.wfs.kd.nbzkpts % world.size == 0 # txt file if world.rank != 0: txt = devnull elif isinstance(txt, str): txt = open(txt, 'w', 1) self.fd = txt self.ecut = ecut / Hartree self.nbands = nbands self.mode = mode self.truncation = truncation if integrate_gamma == 0 and truncation is not None: print('***WARNING*** Analytical Coulomb integration is ' + 'not expected to work with Coulomb truncation. ' + 'Use integrate_gamma=1', file=self.fd) self.integrate_gamma = integrate_gamma self.wfile = wfile self.write_h = write_h self.write_v = write_v # Find q-vectors and weights in the IBZ: self.kd = calc.wfs.kd if -1 in self.kd.bz2bz_ks: print('***WARNING*** Symmetries may not be right ' + 'Use gamma-centered grid to be sure', file=self.fd) offset_c = 0.5 * ((self.kd.N_c + 1) % 2) / self.kd.N_c bzq_qc = monkhorst_pack(self.kd.N_c) + offset_c self.qd = KPointDescriptor(bzq_qc) self.qd.set_symmetry(self.calc.atoms, self.kd.symmetry) self.vol = abs(np.linalg.det(calc.wfs.gd.cell_cv)) # bands self.spins = self.calc.wfs.nspins if self.spins == 2: if self.spinors: self.spinors = False print('***WARNING*** Presently the spinor version' + 'does not work for spin-polarized calculations.' + 'Performing scalar calculation', file=self.fd) assert len(valence_bands[0]) == len(valence_bands[1]) assert len(conduction_bands[0]) == len(conduction_bands[1]) if valence_bands is None: nv = self.calc.wfs.setups.nvalence valence_bands = [[nv // 2 - 1]] if self.spins == 2: valence_bands *= 2 if conduction_bands is None: conduction_bands = [[valence_bands[-1] + 1]] if self.spins == 2: conduction_bands *= 2 self.val_sn = np.array(valence_bands) if len(np.shape(self.val_sn)) == 1: self.val_sn = np.array([self.val_sn]) self.con_sn = np.array(conduction_bands) if len(np.shape(self.con_sn)) == 1: self.con_sn = np.array([self.con_sn]) self.td = True for n in self.val_sn[0]: if n in self.con_sn[0]: self.td = False if len(self.val_sn) == 2: for n in self.val_sn[1]: if n in self.con_sn[1]: self.td = False self.nv = len(self.val_sn[0]) self.nc = len(self.con_sn[0]) if eshift is not None: eshift /= Hartree if gw_skn is not None: assert self.nv + self.nc == len(gw_skn[0, 0]) assert self.kd.nibzkpts == len(gw_skn[0]) gw_skn = gw_skn[:, self.kd.bz2ibz_k] # assert self.kd.nbzkpts == len(gw_skn[0]) gw_skn /= Hartree self.gw_skn = gw_skn self.eshift = eshift # Number of pair orbitals self.nS = self.kd.nbzkpts * self.nv * self.nc * self.spins self.nS *= (self.spinors + 1)**2 # Wigner-Seitz stuff if self.truncation == 'wigner-seitz': self.wstc = WignerSeitzTruncatedCoulomb(self.calc.wfs.gd.cell_cv, self.kd.N_c, self.fd) else: self.wstc = None self.print_initialization(self.td, self.eshift, self.gw_skn)
from ase import * from ase.lattice import bulk from ase.dft import monkhorst_pack from ase.parallel import paropen from gpaw import * from gpaw.wavefunctions.pw import PW from gpaw.xc.exx import EXX # Monkhorst-Pack grid shifted to be gamma centered k = 8 kpts = monkhorst_pack([k, k, k]) kpts += [1. / (2 * k), 1. / ( 2 * k), 1. / (2 * k)] cell = bulk('C', 'fcc', a=3.553).get_cell() a = Atoms('C2', cell=cell, pbc=True, scaled_positions=((0, 0, 0), (0.25, 0.25, 0.25))) calc = GPAW(mode=PW(600), xc='PBE', occupations=FermiDirac(width=0.01), convergence={'density': 1.e-6}, kpts=kpts, txt='diamond_pbe.txt', ) a.set_calculator(calc) E_pbe = a.get_potential_energy() exx = EXX(calc, txt='diamond_exx.txt') exx.calculate() E_hf = exx.get_total_energy()
from ase.dft import monkhorst_pack assert [0, 0, 0] in monkhorst_pack((1, 3, 5)).tolist() assert [0, 0, 0] not in monkhorst_pack((1, 3, 6)).tolist() assert len(monkhorst_pack((3, 4, 6))) == 3 * 4 * 6 from ase.units import Hartree, Bohr, kJ, mol, kcal, kB, fs print Hartree, Bohr, kJ/mol, kcal/mol, kB*300, fs, 1/fs from ase.structure import bulk ru = bulk('Ru', 'hcp', a=2.7) * (2, 2, 1) assert abs(ru.get_distance(0, 7, mic=True) - ru.get_distance(1, 6)) < 1e-14 assert abs(ru.get_distance(0, 5, mic=True) - 2.7) < 1e-14
def kpts(self, kpts): if not hasattr(self, '_kpts') or kpts != self._kpts: self._kpts = kpts self.kpts_kc = monkhorst_pack(self.kpts) if hasattr(self, 'im_r'): del self.im_r # we'll have to recalculate
old_cell = GR.get_cell() old_cell[2, 2] = 2 * c Graphite.set_cell(old_cell) BNNB.set_pbc((True, True, True)) old_cell = BN.get_cell() old_cell[2, 2] = 2 * c BNNB.set_cell(old_cell) BNNB.center() GRBN.set_pbc((True, True, True)) old_cell = BN.get_cell() old_cell[2, 2] = (NGr + NBN) * c GRBN.set_cell(old_cell) atoms = GRBN from ase.dft import monkhorst_pack calc = GPAW( h=0.18, mode=PW(600), kpts=monkhorst_pack((29, 29, 1)) + np.array([0., 0., 0.]), xc='PBE', occupations=FermiDirac(0.01), parallel={'band': 1}, ) atoms.set_calculator(calc) ncpus = 16