Example #1
0
    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)
Example #2
0
    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
Example #3
0
    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)
Example #4
0
    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)
Example #5
0
    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
Example #6
0
    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)
Example #7
0
    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
Example #8
0
    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
Example #9
0
    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
Example #10
0
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
Example #11
0
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
Example #12
0
    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()
Example #13
0
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
Example #14
0
    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
Example #15
0
# 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'
Example #16
0
    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)