def __init__(self, hirshfeld=None, vdwradii=None, calculator=None, Rmax=10, # maximal radius for periodic calculations vdWDB_alphaC6=vdWDB_alphaC6, txt=None): """Constructor Parameters ========== hirshfeld: the Hirshfeld partitioning object calculator: the calculator to get the PBE energy """ self.hirshfeld = hirshfeld if calculator is None: self.calculator = self.hirshfeld.get_calculator() else: self.calculator = calculator if txt is None: txt = get_logging_file_descriptor(self.calculator) self.txt = convert_string_to_fd(txt) self.vdwradii = vdwradii self.vdWDB_alphaC6 = vdWDB_alphaC6 self.Rmax = Rmax self.atoms = None self.sR = 0.94 self.d = 20 Calculator.__init__(self)
def __init__(self, atoms, Excitations, indices=None, gsname='rraman', # name for ground state calculations exname=None, # name for excited state calculations delta=0.01, nfree=2, directions=None, approximation='Profeta', observation={'geometry': '-Z(XX)Z'}, exkwargs={}, # kwargs to be passed to Excitations exext='.ex.gz', # extension for Excitation names txt='-', verbose=False,): assert(nfree == 2) Vibrations.__init__(self, atoms, indices, gsname, delta, nfree) self.name = gsname + '-d%.3f' % delta if exname is None: exname = gsname self.exname = exname + '-d%.3f' % delta self.exext = exext if directions is None: self.directions = np.array([0, 1, 2]) else: self.directions = np.array(directions) self.approximation = approximation self.observation = observation self.exobj = Excitations self.exkwargs = exkwargs self.timer = Timer() self.txt = convert_string_to_fd(txt) self.verbose = verbose
def __init__(self, selection, qmcalc, mmcalc, interaction, vacuum=None, embedding=None, output=None): """EIQMMM object. The energy is calculated as:: _ _ _ _ E = E (R ) + E (R ) + E (R , R ) QM QM MM MM I QM MM parameters: selection: list of int, slice object or list of bool Selection out of all the atoms that belong to the QM part. qmcalc: Calculator object QM-calculator. mmcalc: Calculator object MM-calculator. interaction: Interaction object Interaction between QM and MM regions. vacuum: float or None Amount of vacuum to add around QM atoms. Use None if QM calculator doesn't need a box. embedding: Embedding object or None Specialized embedding object. Use None in order to use the default one. output: None, '-', str or file-descriptor. File for logging information - default is no logging (None). """ self.selection = selection self.qmcalc = qmcalc self.mmcalc = mmcalc self.interaction = interaction self.vacuum = vacuum self.embedding = embedding self.qmatoms = None self.mmatoms = None self.mask = None self.center = None # center of QM atoms in QM-box self.name = '{0}+{1}+{2}'.format(qmcalc.name, interaction.name, mmcalc.name) self.output = convert_string_to_fd(output) Calculator.__init__(self)
def __init__( self, hirshfeld=None, vdwradii=None, calculator=None, Rmax=10., # maximal radius for periodic calculations Ldecay=1., # decay length for the smoothing in periodic calculations vdWDB_alphaC6=vdWDB_alphaC6, txt=None, sR=None): """Constructor Parameters ========== hirshfeld: the Hirshfeld partitioning object calculator: the calculator to get the PBE energy """ self.hirshfeld = hirshfeld if calculator is None: self.calculator = self.hirshfeld.get_calculator() else: self.calculator = calculator if txt is None: txt = get_logging_file_descriptor(self.calculator) self.txt = convert_string_to_fd(txt) self.vdwradii = vdwradii self.vdWDB_alphaC6 = vdWDB_alphaC6 self.Rmax = Rmax self.Ldecay = Ldecay self.atoms = None if sR is None: try: xc_name = self.calculator.get_xc_functional() self.sR = sR_opt[xc_name] except KeyError: raise ValueError( 'Tkatchenko-Scheffler dispersion correction not implemented for %s functional' % xc_name) else: self.sR = sR self.d = 20 Calculator.__init__(self)
def __init__( self, atoms, Excitations, indices=None, gsname='rraman', # name for ground state calculations exname=None, # name for excited state calculations delta=0.01, nfree=2, directions=None, approximation='Profeta', observation={'geometry': '-Z(XX)Z'}, exkwargs={}, # kwargs to be passed to Excitations exext='.ex.gz', # extension for Excitation names txt='-', verbose=False, ): assert (nfree == 2) Vibrations.__init__(self, atoms, indices, gsname, delta, nfree) self.name = gsname + '-d%.3f' % delta if exname is None: exname = gsname self.exname = exname + '-d%.3f' % delta self.exext = exext if directions is None: self.directions = np.array([0, 1, 2]) else: self.directions = np.array(directions) self.approximation = approximation self.observation = observation self.exobj = Excitations self.exkwargs = exkwargs self.timer = Timer() self.txt = convert_string_to_fd(txt) self.verbose = verbose
def __init__(self, hirshfeld=None, vdwradii=None, calculator=None, Rmax=10, # maximal radius for periodic calculations vdWDB_alphaC6=vdWDB_alphaC6, txt=None, sR=None): """Constructor Parameters ========== hirshfeld: the Hirshfeld partitioning object calculator: the calculator to get the PBE energy """ self.hirshfeld = hirshfeld if calculator is None: self.calculator = self.hirshfeld.get_calculator() else: self.calculator = calculator if txt is None: txt = get_logging_file_descriptor(self.calculator) self.txt = convert_string_to_fd(txt) self.vdwradii = vdwradii self.vdWDB_alphaC6 = vdWDB_alphaC6 self.Rmax = Rmax self.atoms = None if sR is None: try: xc_name = self.calculator.get_xc_functional() self.sR = sR_opt[xc_name] except KeyError: raise ValueError('Tkatchenko-Scheffler dispersion correction not implemented for %s functional' % xc_name) else: self.sR = sR self.d = 20 Calculator.__init__(self)
def __init__( self, atoms, # XXX do we need atoms at this stage ? *args, name='raman', exname=None, exext='.alpha', txt='-', verbose=False, comm=world, **kwargs): """ Parameters ---------- atoms: ase Atoms object exext: string Extension for excitation filenames txt: Output stream verbose: Verbosity level of output comm: Communicator, default world """ self.atoms = atoms self.name = name if exname is None: self.exname = name else: self.exname = exname self.exext = exext self.timer = Timer() self.txt = convert_string_to_fd(txt) self.verbose = verbose self.comm = comm
def __init__(self, atoms, Excitations, indices=None, gsname='rraman', # name for ground state calculations exname=None, # name for excited state calculations delta=0.01, nfree=2, directions=None, observation={'geometry': '-Z(XX)Z'}, form='v', # form of the dipole operator exkwargs={}, # kwargs to be passed to Excitations exext='.ex.gz', # extension for Excitation names txt='-', verbose=False, overlap=False, minoverlap=0.02, minrep=0.8, comm=world, ): """ Parameters ---------- atoms: ase Atoms object Excitations: class Type of the excitation list object. The class object is initialized as:: Excitations(atoms.get_calculator()) or by reading form a file as:: Excitations('filename', **exkwargs) The file is written by calling the method Excitations.write('filename'). Excitations should work like a list of ex obejects, where: ex.get_dipole_me(form='v'): gives the velocity form dipole matrix element in units |e| * Angstrom ex.energy: is the transition energy in Hartrees indices: list gsname: string name for ground state calculations exname: string name for excited state calculations delta: float Finite difference displacement in Angstrom. nfree: float directions: approximation: string Level of approximation used. observation: dict Polarization settings form: string Form of the dipole operator, 'v' for velocity form (default) and 'r' for length form. exkwargs: dict Arguments given to the Excitations objects in reading. exext: string Extension for filenames of Excitation lists. txt: Output stream verbose: Verbosity level of output overlap: bool or function Use wavefunction overlaps. minoverlap: float ord dict Minimal absolute overlap to consider. Defaults to 0.02 to avoid numerical garbage. minrep: float Minimal represention to consider derivative, defaults to 0.8 """ assert(nfree == 2) Vibrations.__init__(self, atoms, indices, gsname, delta, nfree) self.name = gsname + '-d%.3f' % delta if exname is None: exname = gsname self.exname = exname + '-d%.3f' % delta self.exext = exext if directions is None: self.directions = np.array([0, 1, 2]) else: self.directions = np.array(directions) self.observation = observation self.exobj = Excitations self.exkwargs = exkwargs self.dipole_form = form self.timer = Timer() self.txt = convert_string_to_fd(txt) self.verbose = verbose self.overlap = overlap if not isinstance(minoverlap, dict): # assume it's a number self.minoverlap = {'orbitals': minoverlap, 'excitations': minoverlap} else: self.minoverlap = minoverlap self.minrep = minrep self.comm = comm
def __init__(self, calc, atoms, charge_regions=[], charges=None, spin_regions=[], spins=None, charge_coefs=None, spin_coefs=None, promolecular_constraint=False, txt='-', minimizer_options={'gtol': 0.01}, Rc={}, mu={ 'Li': 0.5, 'F': 0.7, 'O': 0.7, 'V': 0.5 }, method='BFGS', forces='analytical', use_charge_difference=False, compute_forces=True, maxstep=100, tol=1e-3, bounds=None): """Constrained DFT calculator. calc: GPAW instance DFT calculator object to be constrained. charge_regions: list of list of int Atom indices of atoms in the different charge_regions. spin_regions: list of list of int Atom indices of atoms in the different spin_regions. charges: list of float constrained charges in the different charge_regions. spins: list of float constrained spins in the different charge_regions. Value of 1 sets net magnetisation of one up/alpha electron charge_coefs: list of float Initial values for charge constraint coefficients (eV). spin_coefs: list of float Initial values for spin constraint coefficients (eV). promolecular_constraint: bool Define charge and/or spin constraints from promolecular densities, see: dx.doi.org/10.1021/cr200148b Eq. 29-31 If true, user specified charges/spins are overwritten! The atoms (of Atoms object) specifying the charge/spin regions need to contain have correct charge/spin state! (atoms.set_initial_charges([atomic_charges]) and atoms.set_initial_magnetic_moments([moments])) txt: None or str or file descriptor Log file. Default id '-' meaning standard out. Use None for no output. minimizer_options: dict options for scipy optimizers, see:scipy.optimize.minimize method: str One of scipy optimizers, e.g., BFGS, CG forces: str cDFT weight function contribution to forces 'fd' for finite difference or 'analytical' difference: bool If True, constrain charge difference between two regions Then charge_regions needs two regions and charges needs only one item which is the charge difference between the two regions, the first beign donor, the second acceptor If False, each region is treated with the corresponding charge constraint compute_forces: bool Should the forces be computed? """ Calculator.__init__(self) self.calc = calc self.log = convert_string_to_fd(txt) self.method = method self.forces = forces self.options = minimizer_options self.difference = use_charge_difference self.compute_forces = compute_forces self.Rc = Rc self.mu = mu # set charge constraints and lagrangians self.v_i = np.empty(shape=(0, 0)) self.constraints = np.empty(shape=(0, 0)) self.charge_regions = charge_regions self.spin_regions = spin_regions self._n_charge_regions = len(self.charge_regions) self._n_spin_regions = len(self.spin_regions) self._n_regions = self._n_charge_regions + self._n_spin_regions self.max_step = maxstep self.tol = tol self.gtol = minimizer_options['gtol'] self.bounds = bounds if self.bounds is not None: self.bounds = np.asarray(self.bounds) / Hartree if self.difference: # difference calculation only for 2 charge regions if self.n_spin_regions != 0 or self._n_charge_regions != 2: raise ValueError('No spin constraints ' 'for charge difference calculations and' ' only two regions allowed') assert (self.n_charge_regions == 1) if self.n_charge_regions == 0: self.regions = [] else: self.charge_i = np.array(charges, dtype=float) if charge_coefs is None: # to Hartree self.v_i = 0.1 * np.sign(self.charge_i) else: self.v_i = np.array(charge_coefs) / Hartree if not self.difference: self.regions = copy.copy(self.charge_regions) # The objective is to constrain the number of electrons (nel) # in a certain region --> convert charge to nel Zn = np.zeros(self.n_charge_regions) for j in range(len(Zn)): for atom in atoms[self.charge_regions[j]]: Zn[j] += atom.number # combined spin and charge constraints self.constraints = Zn - self.charge_i else: # constrain charge between two regions nD = 0. # neutral donor nA = 0. # neutral acceptor for atom in atoms[charge_regions[0]]: nD += atom.number for atom in atoms[charge_regions[1]]: nA += atom.number self.dn_core = nD - nA # difference of core self.constraints = [self.dn_core - charges[0]] self.regions = charge_regions # set spin constraints if self.n_spin_regions != 0 and not self.difference: spin_i = np.array(spins, dtype=float) self.constraints = np.append(self.constraints, spin_i) if spin_coefs is None: # to Hartree v_is = 0.1 * np.sign(spin_i) else: v_is = np.array(spin_coefs) / Hartree self.v_i = np.append(self.v_i, v_is) #self.regions.append(spin_regions) [ self.regions.append(self.spin_regions[i]) for i in range(self.n_spin_regions) ] # initialise without v_ext atoms.set_calculator(self.calc) atoms.get_potential_energy() assert atoms.calc.wfs.nspins == 2 self.cdft_initialised = False self.atoms = atoms self.gd = self.calc.density.finegd if promolecular_constraint: self.constraints = get_promolecular_constraints( calc=self.calc, atoms=self.atoms, charge_regions=self.charge_regions, spin_regions=self.spin_regions, charges=charges, spins=spins) # get number of core electrons at each constrained region # used for pseudo free energy to neglect core contributions # in coupling calculation self.n_core_electrons = np.zeros((len(self.regions))) for a in self.atoms: for r in range(len(self.regions[:self.n_charge_regions])): if a.index in self.regions[r] and not self.difference: n_core = a.number - self.calc.wfs.setups[a.index].Nv self.n_core_electrons[r] += n_core elif a.index in self.regions[r] and self.difference: if r == 0: n_core = a.number - self.calc.wfs.setups[a.index].Nv self.n_core_electrons[r] += n_core else: n_core = a.number - self.calc.wfs.setups[a.index].Nv self.n_core_electrons[r] -= n_core w = WeightFunc(self.gd, self.atoms, None, self.Rc, self.mu) self.Rc, self.mu = w.get_Rc_and_mu() # construct cdft potential self.ext = CDFTPotential(regions=self.regions, gd=self.gd, atoms=self.atoms, constraints=self.constraints, n_charge_regions=self.n_charge_regions, difference=self.difference, txt=self.log, vi=self.v_i, Rc=self.Rc, mu=self.mu) self.calc.set(external=self.ext) self.w = self.ext.w_ig
def bandgap(calc=None, direct=False, spin=None, output='-', eigenvalues=None, efermi=None, kpts=None): """Calculates the band-gap. Parameters: calc: Calculator object Electronic structure calculator object. direct: bool Calculate direct band-gap. spin: int or None For spin-polarized systems, you can use spin=0 or spin=1 to look only at a single spin-channel. output: file descriptor Use output=None for no text output or '-' for stdout (default). eigenvalues: ndarray of shape (nspin, nkpt, nband) or (nkpt, nband) Eigenvalues. efermi: float Fermi level (defaults to 0.0). kpts: ndarray of shape (nkpt, 3) For pretty text output only. Returns a (gap, p1, p2) tuple where p1 and p2 are tuples of indices of the valence and conduction points (s, k, n). Example: >>> gap, p1, p2 = bandgap(silicon.calc) Gap: 1.2 eV Transition (v -> c): [0.000, 0.000, 0.000] -> [0.500, 0.500, 0.000] >>> print(gap, p1, p2) 1.2 (0, 0, 3), (0, 5, 4) >>> gap, p1, p2 = bandgap(silicon.calc, direct=True) Direct gap: 3.4 eV Transition at: [0.000, 0.000, 0.000] >>> print(gap, p1, p2) 3.4 (0, 0, 3), (0, 0, 4) """ if calc: kpts = calc.get_ibz_k_points() nk = len(kpts) ns = calc.get_number_of_spins() eigenvalues = np.array( [[calc.get_eigenvalues(kpt=k, spin=s) for k in range(nk)] for s in range(ns)]) if efermi is None: efermi = calc.get_fermi_level() efermi = efermi or 0.0 e_skn = eigenvalues - efermi if eigenvalues.ndim == 2: e_skn = e_skn[np.newaxis] # spinors if not np.isfinite(e_skn).all(): raise ValueError('Bad eigenvalues!') gap, (s1, k1, n1), (s2, k2, n2) = _bandgap(e_skn, spin, direct) if output is not None: def skn(s, k, n): """Convert k or (s, k) to string.""" if kpts is None: return '(s={}, k={}, n={})'.format(s, k, n) return '(s={}, k={}, n={}, [{:.2f}, {:.2f}, {:.2f}])'.format( s, k, n, *kpts[k]) p = functools.partial(print, file=convert_string_to_fd(output)) if spin is not None: p('spin={}: '.format(spin), end='') if gap == 0.0: p('No gap') elif direct: p('Direct gap: {:.3f} eV'.format(gap)) if s1 == s2: p('Transition at:', skn(s1, k1, n1)) else: p('Transition at:', skn('{}->{}'.format(s1, s2), k1, n1)) else: p('Gap: {:.3f} eV'.format(gap)) p('Transition (v -> c):') p(' ', skn(s1, k1, n1), '->', skn(s2, k2, n2)) if eigenvalues.ndim != 3: p1 = (k1, n1) p2 = (k2, n2) else: p1 = (s1, k1, n1) p2 = (s2, k2, n2) return gap, p1, p2
def bandgap(calc=None, direct=False, spin=None, output='-', eigenvalues=None, efermi=None, kpts=None): """Calculates the band-gap. Parameters: calc: Calculator object Electronic structure calculator object. direct: bool Calculate direct band-gap. spin: int or None For spin-polarized systems, you can use spin=0 or spin=1 to look only at a single spin-channel. output: file descriptor Use output=None for no text output or '-' for stdout (default). eigenvalues: ndarray of shape (nspin, nkpt, nband) or (nkpt, nband) Eigenvalues. efermi: float Fermi level (defaults to 0.0). kpts: ndarray of shape (nkpt, 3) For pretty text output only. Returns a (gap, p1, p2) tuple where p1 and p2 are tuples of indices of the valence and conduction points (s, k, n). Example: >>> gap, p1, p2 = bandgap(silicon.calc) Gap: 1.2 eV Transition (v -> c): [0.000, 0.000, 0.000] -> [0.500, 0.500, 0.000] >>> print(gap, p1, p2) 1.2 (0, 0, 3), (0, 5, 4) >>> gap, p1, p2 = bandgap(silicon.calc, direct=True) Direct gap: 3.4 eV Transition at: [0.000, 0.000, 0.000] >>> print(gap, p1, p2) 3.4 (0, 0, 3), (0, 0, 4) """ if calc: kpts = calc.get_ibz_k_points() nk = len(kpts) ns = calc.get_number_of_spins() eigenvalues = np.array( [[calc.get_eigenvalues(kpt=k, spin=s) for k in range(nk)] for s in range(ns)]) if efermi is None: efermi = calc.get_fermi_level() efermi = efermi or 0.0 e_skn = eigenvalues - efermi if eigenvalues.ndim != 3: e_skn = e_skn[np.newaxis] ns, nk, nb = e_skn.shape N_sk = (e_skn < 0.0).sum(2) # number of occupied bands e_skn = np.array( [[e_skn[s, k, N_sk[s, k] - 1:N_sk[s, k] + 1] for k in range(nk)] for s in range(ns)]) ev_sk = e_skn[:, :, 0] # valence band ec_sk = e_skn[:, :, 1] # conduction band if ns == 1: s1 = 0 s2 = 0 gap, k1, n1, k2, n2 = find_gap(N_sk, ev_sk[0], ec_sk[0], direct) elif spin is None: gap, k1, n1, k2, n2 = find_gap(N_sk, ev_sk.ravel(), ec_sk.ravel(), direct) if direct: # Check also spin flips: for s in [0, 1]: g, k, n, _, _ = find_gap(N_sk, ev_sk[s], ec_sk[1 - s], direct) if g < gap: gap = g k1 = k n1 = n k2 = k + nk n2 = n + 1 if gap > 0.0: s1, k1 = divmod(k1, nk) s2, k2 = divmod(k2, nk) else: s1 = None s2 = None else: gap, k1, n1, k2, n2 = find_gap(N_sk[spin:spin + 1], ev_sk[spin], ec_sk[spin], direct) s1 = spin s2 = spin if output is not None: def skn(s, k, n): """Convert k or (s, k) to string.""" if kpts is None: return '(s={}, k={}, n={})'.format(s, k, n) return '(s={}, k={}, n={}, [{:.3f}, {:.3f}, {:.3f}])'.format( s, k, n, *kpts[k]) p = functools.partial(print, file=convert_string_to_fd(output)) if spin is not None: p('spin={}: '.format(spin), end='') if gap == 0.0: p('No gap!') elif direct: p('Direct gap: {:.3f} eV'.format(gap)) if s1 == s2: p('Transition at:', skn(s1, k1, n1)) else: p('Transition at:', skn('{}->{}'.format(s1, s2), k1, n1)) else: p('Gap: {:.3f} eV'.format(gap)) p('Transition (v -> c):') p(' ', skn(s1, k1, n1), '->', skn(s2, k2, n2)) if eigenvalues.ndim != 3: p1 = (k1, n1) p2 = (k2, n2) else: p1 = (s1, k1, n1) p2 = (s2, k2, n2) return gap, p1, p2
def __init__(self, calculator=None, kss=None, xc=None, derivativeLevel=None, numscale=0.001, filehandle=None, txt=None, finegrid=2, eh_comm=None): if not txt and calculator: txt = calculator.log.fd self.txt = convert_string_to_fd(txt, mpi.world) if eh_comm is None: eh_comm = mpi.serial_comm self.eh_comm = eh_comm if filehandle is not None: self.kss = kss self.read(fh=filehandle) return None self.fullkss = kss self.finegrid = finegrid if calculator is None: return self.paw = calculator wfs = self.paw.wfs # handle different grid possibilities self.restrict = None # self.poisson = PoissonSolver(nn=self.paw.hamiltonian.poisson.nn) self.poisson = calculator.hamiltonian.poisson if finegrid: self.poisson.set_grid_descriptor(self.paw.density.finegd) self.poisson.initialize() self.gd = self.paw.density.finegd if finegrid == 1: self.gd = wfs.gd else: self.poisson.set_grid_descriptor(wfs.gd) self.poisson.initialize() self.gd = wfs.gd self.restrict = Transformer(self.paw.density.finegd, wfs.gd, self.paw.density.stencil).apply if xc == 'RPA': xc = None # enable RPA as keyword if xc is not None: self.xc = XC(xc) self.xc.initialize(self.paw.density, self.paw.hamiltonian, wfs, self.paw.occupations) # check derivativeLevel if derivativeLevel is None: derivativeLevel = \ self.xc.get_functional().get_max_derivative_level() self.derivativeLevel = derivativeLevel else: self.xc = None self.numscale = numscale self.singletsinglet = False if kss.nvspins < 2 and kss.npspins < 2: # this will be a singlet to singlet calculation only self.singletsinglet = True nij = len(kss) self.Om = np.zeros((nij, nij)) self.get_full()
def bandgap(calc=None, direct=False, spin=None, output='-', eigenvalues=None, efermi=None, kpts=None): """Calculates the band-gap. Parameters: calc: Calculator object Electronic structure calculator object. direct: bool Calculate direct band-gap. spin: int or None For spin-polarized systems, you can use spin=0 or spin=1 to look only at a single spin-channel. output: file descriptor Use output=None for no text output or '-' for stdout (default). eigenvalues: ndarray of shape (nspin, nkpt, nband) or (nkpt, nband) Eigenvalues. efermi: float Fermi level (defaults to 0.0). kpts: ndarray of shape (nkpt, 3) For pretty text output only. Returns a (gap, p1, p2) tuple where p1 and p2 are tuples of indices of the valence and conduction points (s, k, n). Example: >>> gap, p1, p2 = bandgap(silicon.calc) Gap: 1.2 eV Transition (v -> c): [0.000, 0.000, 0.000] -> [0.500, 0.500, 0.000] >>> print(gap, p1, p2) 1.2 (0, 0, 3), (0, 5, 4) >>> gap, p1, p2 = bandgap(silicon.calc, direct=True) Direct gap: 3.4 eV Transition at: [0.000, 0.000, 0.000] >>> print(gap, p1, p2) 3.4 (0, 0, 3), (0, 0, 4) """ if calc: kpts = calc.get_ibz_k_points() nk = len(kpts) ns = calc.get_number_of_spins() eigenvalues = np.array([[calc.get_eigenvalues(kpt=k, spin=s) for k in range(nk)] for s in range(ns)]) if efermi is None: efermi = calc.get_fermi_level() efermi = efermi or 0.0 e_skn = eigenvalues - efermi if eigenvalues.ndim != 3: e_skn = e_skn[np.newaxis] ns, nk, nb = e_skn.shape N_sk = (e_skn < 0.0).sum(2) # number of occupied bands e_skn = np.array([[e_skn[s, k, N_sk[s, k] - 1:N_sk[s, k] + 1] for k in range(nk)] for s in range(ns)]) ev_sk = e_skn[:, :, 0] # valence band ec_sk = e_skn[:, :, 1] # conduction band if ns == 1: s1 = 0 s2 = 0 gap, k1, n1, k2, n2 = find_gap(N_sk, ev_sk[0], ec_sk[0], direct) elif spin is None: gap, k1, n1, k2, n2 = find_gap(N_sk, ev_sk.ravel(), ec_sk.ravel(), direct) if gap > 0.0: s1, k1 = divmod(k1, nk) s2, k2 = divmod(k2, nk) else: s1 = None s2 = None else: gap, k1, n1, k2, n2 = find_gap(N_sk[spin:spin + 1], ev_sk[spin], ec_sk[spin], direct) s1 = spin s2 = spin if output is not None: def skn(s, k, n): """Convert k or (s, k) to string.""" if kpts is None: return '(s={}, k={}, n={})'.format(s, k, n) return '(s={}, k={}, n={}, [{:.3f}, {:.3f}, {:.3f}])'.format( s, k, n, *kpts[k]) p = functools.partial(print, file=convert_string_to_fd(output)) if spin is not None: p('spin={}: '.format(spin), end='') if gap == 0.0: p('No gap!') elif direct: p('Direct gap: {:.3f} eV'.format(gap)) p('Transition at:', skn(s1, k1, n1)) else: p('Gap: {:.3f} eV'.format(gap)) p('Transition (v -> c):') p(' ', skn(s1, k1, n1), '->', skn(s2, k2, n2)) if eigenvalues.ndim != 3: p1 = (k1, n1) p2 = (k2, n2) else: p1 = (s1, k1, n1) p2 = (s2, k2, n2) return gap, p1, p2
def __init__(self, lrtddft=None, index=0, d=0.001, txt=None, parallel=0, communicator=None, name=None, restart=None): """ExcitedState object. parallel: Can be used to parallelize the numerical force calculation over images. """ self.timer = Timer() self.atoms = None if isinstance(index, int): self.index = UnconstraintIndex(index) else: self.index = index self.results = {} self.results['forces'] = None self.results['energy'] = None if communicator is None: try: communicator = lrtddft.calculator.wfs.world except: communicator = mpi.world self.world = communicator if restart is not None: self.read(restart) if txt is None: self.txt = self.lrtddft.txt else: self.txt = convert_string_to_fd(txt, self.world) if lrtddft is not None: self.lrtddft = lrtddft self.calculator = self.lrtddft.calculator self.atoms = self.calculator.atoms self.parameters = self.calculator.parameters if txt is None: self.txt = self.lrtddft.txt else: self.txt = convert_string_to_fd(txt, self.world) self.d = d self.parallel = parallel self.name = name self.log = GPAWLogger(self.world) self.log.fd = self.txt self.reader = None self.calculator.log.fd = self.txt self.log('#', self.__class__.__name__, __version__) self.log('#', self.index) if name: self.log('name=' + name) self.log('# Force displacement:', self.d) self.log
# Test PurePath catches path assert isinstance(p, PurePath) def clean(): if p.exists(): import shutil shutil.rmtree(str(p)) clean() p.mkdir(exist_ok=True) myf = p / 'test.txt' fd = convert_string_to_fd(myf) assert isinstance(fd, io.TextIOBase) fd.close() fd = convert_string_to_fd(str(myf)) assert isinstance(fd, io.TextIOBase) fd.close() # Test reader/writer teststr = 'Teststring!' @writer def mywrite(file, fdcmp=None): assert isinstance(file, io.TextIOBase) assert file.mode == 'w'
def __init__(self, calc, xc='RPA', filename=None, skip_gamma=False, qsym=True, nlambda=None, nfrequencies=16, frequency_max=800.0, frequency_scale=2.0, frequencies=None, weights=None, truncation=None, world=mpi.world, nblocks=1, txt='-'): """Creates the RPACorrelation object calc: str or calculator object The string should refer to the .gpw file contaning KS orbitals xc: str Exchange-correlation kernel. This is only different from RPA when this object is constructed from a different module - e.g. fxc.py filename: str txt output skip_gamme: bool If True, skip q = [0,0,0] from the calculation qsym: bool Use symmetry to reduce q-points nlambda: int Number of lambda points. Only used for numerical coupling constant integration involved when called from fxc.py nfrequencies: int Number of frequency points used in the Gauss-Legendre integration frequency_max: float Largest frequency point in Gauss-Legendre integration frequency_scale: float Determines density of frequency points at low frequencies. A slight increase to e.g. 2.5 or 3.0 improves convergence wth respect to frequency points for metals frequencies: list List of frequancies for user-specified frequency integration weights: list list of weights (integration measure) for a user specified frequency grid. Must be specified and have the same length as frequencies if frequencies is not None truncation: str Coulomb truncation scheme. Can be either wigner-seitz, 2D, 1D, or 0D world: communicator nblocks: int Number of parallelization blocks. Frequency parallelization can be specified by setting nblocks=nfrequencies and is useful for memory consuming calculations txt: str txt file for saving and loading contributions to the correlation energy from different q-points """ if isinstance(calc, str): calc = GPAW(calc, txt=None, communicator=mpi.serial_comm) self.calc = calc self.fd = convert_string_to_fd(txt, world) self.timer = Timer() if frequencies is None: frequencies, weights = get_gauss_legendre_points( nfrequencies, frequency_max, frequency_scale) user_spec = False else: assert weights is not None user_spec = True self.omega_w = frequencies / Hartree self.weight_w = weights / Hartree if nblocks > 1: assert len(self.omega_w) % nblocks == 0 self.nblocks = nblocks self.world = world self.truncation = truncation self.skip_gamma = skip_gamma self.ibzq_qc = None self.weight_q = None self.initialize_q_points(qsym) # Energies for all q-vetors and cutoff energies: self.energy_qi = [] self.filename = filename self.print_initialization(xc, frequency_scale, nlambda, user_spec)