Example #1
0
 def __init__(self, calc, mm, mp, dipoles, finegrid=False): #quadruoles
     # Need QM density (on grid) and MM object for CM
     self.calc = calc # QM calculator
     self.mm   = mm   # SCME atoms object
     self.mp   = mp   # no. atoms per SCME mol
     #
     # Interacting with
     self.dipoles = dipoles
     # self.qpoles = qpoles
     #
     # LOG
     self.timer = Timer()
     self.out   = sys.stdout
     #
     self.cm = None
Example #2
0
    def __init__(self, calculator=None, **kwargs):
        
        self.timer = Timer()

        self.set(**kwargs)

        if isinstance(calculator, str):
            ExcitationList.__init__(self, None, self.txt)
            self.filename = calculator
        else:
            ExcitationList.__init__(self, calculator, self.txt)

        if self.filename is not None:
            return self.read(self.filename)

        if self.eh_comm is None:
            self.eh_comm = mpi.serial_comm
        elif isinstance(self.eh_comm, (mpi.world.__class__,
                                       mpi.serial_comm.__class__)):
            # Correct type already.
            pass
        else:
            # world should be a list of ranks:
            self.eh_comm = mpi.world.new_communicator(np.asarray(eh_comm))
 
        if calculator is not None and calculator.initialized:
            if calculator.wfs.kpt_comm.size > 1:
                err_txt = "Spin parallelization with Linear response "
                err_txt += "TDDFT. Use parallel = {'domain' : 'domain_only'} "
                err_txt += "calculator parameter."
                raise NotImplementedError(err_txt)
            if self.xc == 'GS':
                self.xc = calculator.hamiltonian.xc.name
            calculator.converge_wave_functions()
            if calculator.density.nct_G is None:
                spos_ac = calculator.initialize_positions()
                calculator.wfs.initialize(calculator.density, 
                                          calculator.hamiltonian, spos_ac)

            self.update(calculator)
Example #3
0
    def __init__(self, mm, qm,  mp, calcmm, dyn=False, g=0.5):

        self.mm     = mm     # mm atoms object
        self.mp     = mp     # no. atoms per center of mass (cm)
        self.dyn    = dyn    # dyn. update of potential?
        self.calcmm = calcmm # mm calculator (SCME)
        self.qm     = qm     # qm atoms object
        #
        self.qmidx  = len(qm)# no. QM atoms
        #
        self.nm     = len(self.mm) / self.mp
        self.cm     = self.get_cm(self.nm)
        self.timer  = Timer()
        # smoothing value
        self.g = g

        # initialization
        self.initial = True
        
        # Hold on to old arrays
        self.dipoles = None
        self.qpoles  = None
        self.dipoles_1 = None
        self.qpoles_1  = None
Example #4
0
class LrTDDFT(ExcitationList):
    """Linear Response TDDFT excitation class
    
    Input parameters:

    calculator:
    the calculator object after a ground state calculation
      
    nspins:
    number of spins considered in the calculation
    Note: Valid only for unpolarised ground state calculation

    eps:
    Minimal occupation difference for a transition (default 0.001)

    istart:
    First occupied state to consider
    jend:
    Last unoccupied state to consider
      
    xc:
    Exchange-Correlation approximation in the Kernel
    derivative_level:
    0: use Exc, 1: use vxc, 2: use fxc  if available

    filename:
    read from a file
    """
    def __init__(self, calculator=None, **kwargs):
        
        self.timer = Timer()

        self.set(**kwargs)

        if isinstance(calculator, str):
            ExcitationList.__init__(self, None, self.txt)
            self.filename = calculator
        else:
            ExcitationList.__init__(self, calculator, self.txt)

        if self.filename is not None:
            return self.read(self.filename)

        if self.eh_comm is None:
            self.eh_comm = mpi.serial_comm
        elif isinstance(self.eh_comm, (mpi.world.__class__,
                                       mpi.serial_comm.__class__)):
            # Correct type already.
            pass
        else:
            # world should be a list of ranks:
            self.eh_comm = mpi.world.new_communicator(np.asarray(eh_comm))
 
        if calculator is not None and calculator.initialized:
            if calculator.wfs.kpt_comm.size > 1:
                err_txt = "Spin parallelization with Linear response "
                err_txt += "TDDFT. Use parallel = {'domain' : 'domain_only'} "
                err_txt += "calculator parameter."
                raise NotImplementedError(err_txt)
            if self.xc == 'GS':
                self.xc = calculator.hamiltonian.xc.name
            calculator.converge_wave_functions()
            if calculator.density.nct_G is None:
                spos_ac = calculator.initialize_positions()
                calculator.wfs.initialize(calculator.density, 
                                          calculator.hamiltonian, spos_ac)

            self.update(calculator)

    def set(self, **kwargs):

        defaults = {
            'nspins' : None,
            'eps' : 0.001,
            'istart' : 0,
            'jend' : None,
            'energy_range' : None,
            'xc' : 'GS',
            'derivative_level' : 1,
            'numscale' : 0.00001,
            'txt' : None,
            'filename' : None,
            'finegrid' : 2,
            'force_ApmB' : False, # for tests
            'eh_comm' : None # parallelization over eh-pairs
            }

        changed = False
        for key, value in defaults.items():
            if hasattr(self, key):
                value = getattr(self, key)  # do not overwrite
            setattr(self, key, kwargs.pop(key, value))
            if value != getattr(self, key):
                changed = True

        for key in kwargs:
            raise KeyError('Unknown key ' + key)

        return changed

    def set_calculator(self, calculator):
        self.calculator = calculator
#        self.force_ApmB = parameters['force_ApmB']
        self.force_ApmB = None # XXX

    def analyse(self, what=None, out=None, min=0.1):
        """Print info about the transitions.
        
        Parameters:
          1. what: I list of excitation indicees, None means all
          2. out : I where to send the output, None means sys.stdout
          3. min : I minimal contribution to list (0<min<1)
        """
        if what is None:
            what = range(len(self))
        elif isinstance(what, int):
            what = [what]

        if out is None:
            out = sys.stdout
            
        for i in what:
            print >> out, str(i) + ':', self[i].analyse(min=min)
            
    def update(self, calculator=None, **kwargs):

        changed = self.set(**kwargs)
        if calculator is not None:
            changed = True
            self.set_calculator(calculator)

        if not changed:
            return

        self.forced_update()

    def forced_update(self):
        """Recalc yourself."""
        nonselfconsistent_xc = None
        if not self.force_ApmB:
            Om = OmegaMatrix
            name = 'LrTDDFT'
            if self.xc:
                xc = XC(self.xc)
                if hasattr(xc, 'hybrid') and xc.hybrid > 0.0:
                    Om = ApmB
                    name = 'LrTDDFThyb'
#                    nonselfconsistent_xc = HybridXC('PBE0', alpha=5.0)
        else:
            Om = ApmB
            name = 'LrTDDFThyb'

        self.kss = KSSingles(calculator=self.calculator,
                             nspins=self.nspins,
                             nonselfconsistent_xc=nonselfconsistent_xc,
                             eps=self.eps,
                             istart=self.istart,
                             jend=self.jend,
                             energy_range=self.energy_range,
                             txt=self.txt)

        self.Om = Om(self.calculator, self.kss,
                     self.xc, self.derivative_level, self.numscale,
                     finegrid=self.finegrid, eh_comm=self.eh_comm,
                     txt=self.txt)
        self.name = name

    def diagonalize(self, istart=None, jend=None, 
                    energy_range=None, TDA=False):
        self.timer.start('diagonalize')
        self.timer.start('omega')
        self.Om.diagonalize(istart, jend, energy_range, TDA)
        self.timer.stop('omega')
        
        # remove old stuff
        self.timer.start('clean')
        while len(self): self.pop()
        self.timer.stop('clean')

        print >> self.txt, 'LrTDDFT digonalized:'
        self.timer.start('build')
        for j in range(len(self.Om.kss)):
            self.append(LrTDDFTExcitation(self.Om, j))
            print >> self.txt, ' ', str(self[-1])
        self.timer.stop('build')
        self.timer.stop('diagonalize')

    def get_Om(self):
        return self.Om

    def read(self, filename=None, fh=None):
        """Read myself from a file"""

        if fh is None:
            if filename.endswith('.gz'):
                try:
                    import gzip
                    f = gzip.open(filename)
                except:
                    f = open(filename, 'r')
            else:
                f = open(filename, 'r')
            self.filename = filename
        else:
            f = fh
            self.filename = None

        # get my name
        s = f.readline().replace('\n','')
        self.name = s.split()[1]

        self.xc = f.readline().replace('\n','').split()[0]
        values = f.readline().split()
        self.eps = float(values[0])
        if len(values) > 1:
            self.derivative_level = int(values[1])
            self.numscale = float(values[2])
            self.finegrid = int(values[3])
        else:
            # old writing style, use old defaults
            self.numscale = 0.001

        self.kss = KSSingles(filehandle=f)
        if self.name == 'LrTDDFT':
            self.Om = OmegaMatrix(kss=self.kss, filehandle=f,
                                  txt=self.txt)
        else:
            self.Om = ApmB(kss=self.kss, filehandle=f,
                                  txt=self.txt)
        self.Om.Kss(self.kss)

        # check if already diagonalized
        p = f.tell()
        s = f.readline()
        if s != '# Eigenvalues\n':
            # go back to previous position
            f.seek(p)
        else:
            # load the eigenvalues
            n = int(f.readline().split()[0])
            for i in range(n):
                self.append(LrTDDFTExcitation(string=f.readline()))
            # load the eigenvectors
            f.readline()
            for i in range(n):
                values = f.readline().split()
                weights = [float(val) for val in values]
                self[i].f = np.array(weights)
                self[i].kss = self.kss

        if fh is None:
            f.close()

        # update own variables
        self.istart = self.Om.fullkss.istart
        self.jend = self.Om.fullkss.jend


    def singlets_triplets(self):
        """Split yourself into a singlet and triplet object"""

        slr = LrTDDFT(None, nspins=self.nspins, eps=self.eps,
                      istart=self.istart, jend=self.jend, xc=self.xc, 
                      derivative_level=self.derivative_level, 
                      numscale=self.numscale)
        tlr = LrTDDFT(None, nspins=self.nspins, eps=self.eps,
                      istart=self.istart, jend=self.jend, xc=self.xc, 
                      derivative_level=self.derivative_level, 
                      numscale=self.numscale)
        slr.Om, tlr.Om = self.Om.singlets_triplets()
        for lr in [slr, tlr]:
            lr.kss = lr.Om.fullkss
        return slr, tlr

    def single_pole_approximation(self, i, j):
        """Return the excitation according to the
        single pole approximation. See e.g.:
        Grabo et al, Theochem 501 (2000) 353-367
        """
        for ij, kss in enumerate(self.kss):
            if kss.i == i and kss.j == j:
                return sqrt(self.Om.full[ij][ij]) * Hartree
                return self.Om.full[ij][ij] / kss.energy * Hartree

    def __str__(self):
        string = ExcitationList.__str__(self)
        string += '# derived from:\n'
        string += self.Om.kss.__str__()
        return string

    def write(self, filename=None, fh=None):
        """Write current state to a file.

        'filename' is the filename. If the filename ends in .gz,
        the file is automatically saved in compressed gzip format.

        'fh' is a filehandle. This can be used to write into already
        opened files. 
        """
        if mpi.rank == mpi.MASTER:
            if fh is None:
                if filename.endswith('.gz'):
                    try:
                        import gzip
                        f = gzip.open(filename,'wb')
                    except:
                        f = open(filename, 'w')
                else:
                    f = open(filename, 'w')
            else:
                f = fh

            f.write('# ' + self.name + '\n')
            xc = self.xc
            if xc is None: xc = 'RPA'
            if self.calculator is not None:
                xc += ' ' + self.calculator.get_xc_functional()
            f.write(xc + '\n')
            f.write('%g %d %g %d' % (self.eps, int(self.derivative_level),
                                     self.numscale, int(self.finegrid)) + '\n')
            self.kss.write(fh=f)
            self.Om.write(fh=f)

            if len(self):
                f.write('# Eigenvalues\n')
                istart = self.istart
                if istart is None: 
                    istart = self.kss.istart
                jend = self.jend
                if jend is None: 
                    jend = self.kss.jend
                f.write('%d %d %d'%(len(self), istart, jend) + '\n')
                for ex in self:
                    f.write(ex.outstring())
                f.write('# Eigenvectors\n')
                for ex in self:
                    for w in ex.f:
                        f.write('%g '%w)
                    f.write('\n')

            if fh is None:
                f.close()
Example #5
0
class DipoleDensityForces:

    def __init__(self, calc, mm, mp, dipoles, finegrid=False): #quadruoles
        # Need QM density (on grid) and MM object for CM
        self.calc = calc # QM calculator
        self.mm   = mm   # SCME atoms object
        self.mp   = mp   # no. atoms per SCME mol
        #
        # Interacting with
        self.dipoles = dipoles
        # self.qpoles = qpoles
        #
        # LOG
        self.timer = Timer()
        self.out   = sys.stdout
        #
        self.cm = None

    def calculate_forces(self):
        self.out.write('Calculating E-dens to MM Forces')
        self.timer.start('E-dens to MM Forces')
        #
        if self.cm is None:
            self.get_cm()
        # Force array
        n = len(self.atoms) / self.mp
        F = np.zeros((3,n))
        #
        calc = self.calc
        # Grab grid descriptor and density
        if self.finegd:
            gd = calc.density.finegd
            # Get dens on fg
            if calc.density.nt_sg is None:
                calc.density.interpolate_pseudo_density()
            nt_sg = calc.density.nt_sg
            #
        else:
            gd = calc.density.gd
            nt_sg = calc.density.nt_sG
        #
        if calc.density.nspins == 1:
            nt_g = nt_sg[0]
        else:
            nt_g = nt_sg.sum(axis=1)
        #
        sg = (np.indices(gd.n_c, float).T + \
                                  gd.beg_c) / gd.N_c
        #
        for a, pos in enumerate(self.cm):
            # Get all scaled gpt distance to cm
            asg = sg - np.linalg.solve(gd.cell_cv.T, pos)
            # r(xyz) - in Ang
            xyz = np.dot(asg, gd.cell_cv) * Bohr
            #
            dis = np.sqrt(((xyz.T)**2).sum(axis=0))
            # n(r)/d**3
            n_d = nt_sg / dis**3
            # p*r
            pr_r = xyz.T*np.dot(xyz, self.dipole[a])
            # whole term (with electrostatic constant)  
            tot = k_c * n_d * (self.dipole[a] - 3*pr_r.T).T
            #
            # new = tot.reshape((3,tot[1]*tot[2]*tot[3] in shape)) 
            #
            F[:,a] = [tot[0].sum(), tot[1].sum(), tot[2].sum()]

        self.timer.stop('E-dens to MM Forces')
        self.out.write('  E-dens/MM Forces took: %.3f sec'
                       %self.timer.timers[('E-dens to MM Forces',)])
        return F

    def get_cm(self):
        """ Get CM for SCME mols in units [Bohr]

        """
        #
        atoms = self.mm
        mp = self.mp
        n = len(self.atoms) / self.mp
        cm = np.zeros((n,3))
        #
        for i in range(n):
            cm[i,:] += atoms[i*mp:(i+1)*mp].get_center_of_mass() / Bohr
        #
        self.cm = cm.copy()
Example #6
0
 def test_inv_speed(self):
     full_mat = self.recover()
     timer = Timer()
     timer.start('full_numpy')
     tmp0 = np.linalg.inv(full_mat)
     timer.stop('full_numpy')
     
     timer.start('full_lapack')
     inverse_general(full_mat)
     timer.stop('full_lapack')
     
     timer.start('sparse_lapack')
     self.inv_eq()
     timer.stop('sparse_lapack')
     
     timer.start('sparse_lapack_ne')
     self.inv_ne()
     timer.stop('sparse_lapack_ne')
     
     times = []
     methods = ['full_numpy', 'full_lapack', 'sparse_lapack']
     for name in methods:
         time = timer.timers[name,]
         print name, time
         times.append(time)
     
     mintime = np.min(times)
     self.inv_method = methods[np.argmin(times)]
     print 'mintime', mintime
     
     print  'sparse_lapack_ne', timer.timers['sparse_lapack_ne',]
Example #7
0
import sys

from ase import Atoms, Atom

from gpaw import GPAW
from gpaw.test import equal
from gpaw.utilities.timing import Timer
from gpaw.xc.hybrid import HybridXC

timer = Timer()

loa = Atoms('Be2',
            [(0, 0, 0), (2.45, 0, 0)],
            magmoms=[0.5, 0.5],
            cell=[5.9, 4.8, 5.0])
loa.center()

fgl = [False, True]
#fgl = [True, False]

txt='-'
txt='/dev/null'

E = {}
niter = {}
for fg in fgl:
    if fg:
        tstr = 'Exx on fine grid'
    else:
        tstr = 'Exx on coarse grid'
    timer.start(tstr)
Example #8
0
    def get_rpa(self):
        """calculate RPA part of the omega matrix"""

        # shorthands
        kss=self.fullkss
        finegrid=self.finegrid
        wfs = self.paw.wfs
        eh_comm = self.eh_comm
        
        # calculate omega matrix
        nij = len(kss)
        print >> self.txt,'RPA',nij,'transitions'
        
        Om = self.Om
        
        for ij in range(eh_comm.rank, nij, eh_comm.size):
            print >> self.txt,'RPA kss['+'%d'%ij+']=', kss[ij]

            timer = Timer()
            timer.start('init')
            timer2 = Timer()
                      
            # smooth density including compensation charges
            timer2.start('with_compensation_charges 0')
            rhot_p = kss[ij].with_compensation_charges(
                finegrid is not 0)
            timer2.stop()
            
            # integrate with 1/|r_1-r_2|
            timer2.start('poisson')
            phit_p = np.zeros(rhot_p.shape, rhot_p.dtype.char)
            self.poisson.solve(phit_p, rhot_p, charge=None)
            timer2.stop()

            timer.stop()
            t0 = timer.get_time('init')
            timer.start(ij)

            if finegrid == 1:
                rhot = kss[ij].with_compensation_charges()
                phit = self.gd.zeros()
##                print "shapes 0=",phit.shape,rhot.shape
                self.restrict(phit_p,phit)
            else:
                phit = phit_p
                rhot = rhot_p

            for kq in range(ij,nij):
                if kq != ij:
                    # smooth density including compensation charges
                    timer2.start('kq with_compensation_charges')
                    rhot = kss[kq].with_compensation_charges(
                        finegrid is 2)
                    timer2.stop()

                pre = 2 * sqrt(kss[ij].get_energy() * kss[kq].get_energy() *
                               kss[ij].get_weight() * kss[kq].get_weight()  )
                I = self.Coulomb_integral_kss(kss[ij], kss[kq],
                                              rhot, phit, timer2)
                Om[ij,kq] = pre * I
                    
                if ij == kq:
                    Om[ij,kq] += kss[ij].get_energy()**2
                else:
                    Om[kq,ij]=Om[ij,kq]

            timer.stop()
##            timer2.write()
            if ij < (nij-1):
                t = timer.get_time(ij) # time for nij-ij calculations
                t = .5*t*(nij-ij)  # estimated time for n*(n+1)/2, n=nij-(ij+1)
                print >> self.txt,'RPA estimated time left',\
                      self.timestring(t0*(nij-ij-1)+t)
Example #9
0
    def get_xc(self):
        """Add xc part of the coupling matrix"""

        # shorthands
        paw = self.paw
        wfs = paw.wfs
        gd = paw.density.finegd
        comm = gd.comm
        eh_comm = self.eh_comm
        
        fg = self.finegrid is 2
        kss = self.fullkss
        nij = len(kss)

        Om_xc = self.Om
        # initialize densities
        # nt_sg is the smooth density on the fine grid with spin index

        if kss.nvspins==2:
            # spin polarised ground state calc.
            nt_sg = paw.density.nt_sg
        else:
            # spin unpolarised ground state calc.
            if kss.npspins==2:
                # construct spin polarised densities
                nt_sg = np.array([.5*paw.density.nt_sg[0],
                                  .5*paw.density.nt_sg[0]])
            else:
                nt_sg = paw.density.nt_sg
        # check if D_sp have been changed before
        D_asp = self.paw.density.D_asp
        for a, D_sp in D_asp.items():
            if len(D_sp) != kss.npspins:
                if len(D_sp) == 1:
                    D_asp[a] = np.array([0.5 * D_sp[0], 0.5 * D_sp[0]])
                else:
                    D_asp[a] = np.array([D_sp[0] + D_sp[1]])
                
        # restrict the density if needed
        if fg:
            nt_s = nt_sg
        else:
            nt_s = self.gd.zeros(nt_sg.shape[0])
            for s in range(nt_sg.shape[0]):
                self.restrict(nt_sg[s], nt_s[s])
            gd = paw.density.gd
                
        # initialize vxc or fxc

        if self.derivativeLevel==0:
            raise NotImplementedError
            if kss.npspins==2:
                v_g=nt_sg[0].copy()
            else:
                v_g=nt_sg.copy()
        elif self.derivativeLevel==1:
            pass
        elif self.derivativeLevel==2:
            fxc_sg = np.zeros(nt_sg.shape)
            self.xc.calculate_fxc(gd, nt_sg, fxc_sg)
        else:
            raise ValueError('derivativeLevel can only be 0,1,2')

##        self.paw.my_nuclei = []

        ns=self.numscale
        xc=self.xc
        print >> self.txt, 'XC',nij,'transitions'
        for ij in range(eh_comm.rank, nij, eh_comm.size):
            print >> self.txt,'XC kss['+'%d'%ij+']' 

            timer = Timer()
            timer.start('init')
            timer2 = Timer()
                      
            if self.derivativeLevel >= 1:
                # vxc is available
                # We use the numerical two point formula for calculating
                # the integral over fxc*n_ij. The results are
                # vvt_s        smooth integral
                # nucleus.I_sp atom based correction matrices (pack2)
                #              stored on each nucleus
                timer2.start('init v grids')
                vp_s=np.zeros(nt_s.shape,nt_s.dtype.char)
                vm_s=np.zeros(nt_s.shape,nt_s.dtype.char)
                if kss.npspins == 2: # spin polarised
                    nv_s = nt_s.copy()
                    nv_s[kss[ij].pspin] += ns * kss[ij].get(fg)
                    xc.calculate(gd, nv_s, vp_s)
                    nv_s = nt_s.copy()
                    nv_s[kss[ij].pspin] -= ns * kss[ij].get(fg)
                    xc.calculate(gd, nv_s, vm_s)
                else: # spin unpolarised
                    nv = nt_s + ns * kss[ij].get(fg)
                    xc.calculate(gd, nv, vp_s)
                    nv = nt_s - ns * kss[ij].get(fg)
                    xc.calculate(gd, nv, vm_s)
                vvt_s = (0.5 / ns) * (vp_s - vm_s)
                timer2.stop()

                # initialize the correction matrices
                timer2.start('init v corrections')
                I_asp = {}
                for a, P_ni in wfs.kpt_u[kss[ij].spin].P_ani.items():
                    # create the modified density matrix
                    Pi_i = P_ni[kss[ij].i]
                    Pj_i = P_ni[kss[ij].j]
                    P_ii = np.outer(Pi_i, Pj_i)
                    # we need the symmetric form, hence we can pack
                    P_p = pack(P_ii)
                    D_sp = self.paw.density.D_asp[a].copy()
                    D_sp[kss[ij].pspin] -= ns * P_p
                    setup = wfs.setups[a]
                    I_sp = np.zeros_like(D_sp)
                    self.xc.calculate_paw_correction(setup, D_sp, I_sp)
                    I_sp *= -1.0
                    D_sp = self.paw.density.D_asp[a].copy()
                    D_sp[kss[ij].pspin] += ns * P_p
                    self.xc.calculate_paw_correction(setup, D_sp, I_sp)
                    I_sp /= 2.0 * ns
                    I_asp[a] = I_sp
                timer2.stop()
                    
            timer.stop()
            t0 = timer.get_time('init')
            timer.start(ij)
            
            for kq in range(ij,nij):
                weight = self.weight_Kijkq(ij, kq)
                
                if self.derivativeLevel == 0:
                    # only Exc is available
                    
                    if kss.npspins==2: # spin polarised
                        nv_g = nt_sg.copy()
                        nv_g[kss[ij].pspin] += kss[ij].get(fg)
                        nv_g[kss[kq].pspin] += kss[kq].get(fg)
                        Excpp = xc.get_energy_and_potential(
                            nv_g[0], v_g, nv_g[1], v_g)
                        nv_g = nt_sg.copy()
                        nv_g[kss[ij].pspin] += kss[ij].get(fg)
                        nv_g[kss[kq].pspin] -= kss[kq].get(fg)
                        Excpm = xc.get_energy_and_potential(\
                                            nv_g[0],v_g,nv_g[1],v_g)
                        nv_g = nt_sg.copy()
                        nv_g[kss[ij].pspin] -=\
                                        kss[ij].get(fg)
                        nv_g[kss[kq].pspin] +=\
                                        kss[kq].get(fg)
                        Excmp = xc.get_energy_and_potential(\
                                            nv_g[0],v_g,nv_g[1],v_g)
                        nv_g = nt_sg.copy()
                        nv_g[kss[ij].pspin] -= \
                                        kss[ij].get(fg)
                        nv_g[kss[kq].pspin] -=\
                                        kss[kq].get(fg)
                        Excpp = xc.get_energy_and_potential(\
                                            nv_g[0],v_g,nv_g[1],v_g)
                    else: # spin unpolarised
                        nv_g=nt_sg + ns*kss[ij].get(fg)\
                              + ns*kss[kq].get(fg)
                        Excpp = xc.get_energy_and_potential(nv_g,v_g)
                        nv_g=nt_sg + ns*kss[ij].get(fg)\
                              - ns*kss[kq].get(fg)
                        Excpm = xc.get_energy_and_potential(nv_g,v_g)
                        nv_g=nt_sg - ns*kss[ij].get(fg)\
                              + ns*kss[kq].get(fg)
                        Excmp = xc.get_energy_and_potential(nv_g,v_g)
                        nv_g=nt_sg - ns*kss[ij].get(fg)\
                              - ns*kss[kq].get(fg)
                        Excmm = xc.get_energy_and_potential(nv_g,v_g)

                    Om_xc[ij,kq] += weight *\
                                0.25*(Excpp-Excmp-Excpm+Excmm)/(ns*ns)
                              
                elif self.derivativeLevel == 1:
                    # vxc is available

                    timer2.start('integrate')
                    Om_xc[ij,kq] += weight*\
                                 self.gd.integrate(kss[kq].get(fg)*
                                                   vvt_s[kss[kq].pspin])
                    timer2.stop()

                    timer2.start('integrate corrections')
                    Exc = 0.
                    for a, P_ni in wfs.kpt_u[kss[kq].spin].P_ani.items():
                        # create the modified density matrix
                        Pk_i = P_ni[kss[kq].i]
                        Pq_i = P_ni[kss[kq].j]
                        P_ii = np.outer(Pk_i, Pq_i)
                        # we need the symmetric form, hence we can pack
                        # use pack as I_sp used pack2
                        P_p = pack(P_ii)
                        Exc += np.dot(I_asp[a][kss[kq].pspin], P_p)
                    Om_xc[ij, kq] += weight * self.gd.comm.sum(Exc)
                    timer2.stop()

                elif self.derivativeLevel == 2:
                    # fxc is available
                    if kss.npspins==2: # spin polarised
                        Om_xc[ij,kq] += weight *\
                            gd.integrate(kss[ij].get(fg) *
                                         kss[kq].get(fg) *
                                         fxc_sg[kss[ij].pspin, kss[kq].pspin])
                    else: # spin unpolarised
                        Om_xc[ij,kq] += weight *\
                            gd.integrate(kss[ij].get(fg) *
                                         kss[kq].get(fg) *
                                         fxc_sg)
                    
                    # XXX still numeric derivatives for local terms
                    timer2.start('integrate corrections')
                    Exc = 0.
                    for a, P_ni in wfs.kpt_u[kss[kq].spin].P_ani.items():
                        # create the modified density matrix
                        Pk_i = P_ni[kss[kq].i]
                        Pq_i = P_ni[kss[kq].j]
                        P_ii = np.outer(Pk_i, Pq_i)
                        # we need the symmetric form, hence we can pack
                        # use pack as I_sp used pack2
                        P_p = pack(P_ii)
                        Exc += np.dot(I_asp[a][kss[kq].pspin], P_p)
                    Om_xc[ij, kq] += weight * self.gd.comm.sum(Exc)
                    timer2.stop()

                if ij != kq:
                    Om_xc[kq,ij] = Om_xc[ij,kq]
                
            timer.stop()
##            timer2.write()
            if ij < (nij-1):
                print >> self.txt,'XC estimated time left',\
                    self.time_left(timer, t0, ij, nij)
Example #10
0
    def __init__(self, name, hybrid=None, xc=None,
                 alpha=None,
                 gamma_point=1,
                 method='standard',
                 bandstructure=False,
                 logfilename='-', bands=None,
                 fcut=1e-10,
                 molecule=False,
                 qstride=1,
                 world=None):
        """Mix standard functionals with exact exchange.

        name: str
            Name of functional: EXX, PBE0, HSE03, HSE06
        hybrid: float
            Fraction of exact exchange.
        xc: str or XCFunctional object
            Standard DFT functional with scaled down exchange.
        method: str
            Use 'standard' standard formula and 'acdf for
            adiabatic-connection dissipation fluctuation formula.
        alpha: float
            XXX describe
        gamma_point: bool
            0: Skip k2-k1=0 interactions.
            1: Use the alpha method.
            2: Integrate the gamma point.
        bandstructure: bool
            Calculate bandstructure instead of just the total energy.
        bands: list of int
            List of bands to calculate bandstructure for.  Default is
            all bands.
        molecule: bool
            Decouple electrostatic interactions between periodically
            repeated images.
        fcut: float
            Threshold for empty band.
        """

        self.alpha = alpha
        self.fcut = fcut

        self.gamma_point = gamma_point
        self.method = method
        self.bandstructure = bandstructure
        self.bands = bands

        self.fd = logfilename
        self.write_timing_information = True

        HybridXCBase.__init__(self, name, hybrid, xc)

        # EXX energies:
        self.exx = None  # total
        self.evv = None  # valence-valence (pseudo part)
        self.evvacdf = None  # valence-valence (pseudo part)
        self.devv = None  # valence-valence (PAW correction)
        self.evc = None  # valence-core
        self.ecc = None  # core-core

        self.exx_skn = None  # bandstructure

        self.qlatest = None

        if world is None:
            world = mpi.world
        self.world = world

        self.molecule = molecule
        
        if isinstance(qstride, int):
            qstride = [qstride] * 3
        self.qstride_c = np.asarray(qstride)
        
        self.timer = Timer()
Example #11
0
class HybridXC(HybridXCBase):
    orbital_dependent = True

    def __init__(self, name, hybrid=None, xc=None,
                 alpha=None,
                 gamma_point=1,
                 method='standard',
                 bandstructure=False,
                 logfilename='-', bands=None,
                 fcut=1e-10,
                 molecule=False,
                 qstride=1,
                 world=None):
        """Mix standard functionals with exact exchange.

        name: str
            Name of functional: EXX, PBE0, HSE03, HSE06
        hybrid: float
            Fraction of exact exchange.
        xc: str or XCFunctional object
            Standard DFT functional with scaled down exchange.
        method: str
            Use 'standard' standard formula and 'acdf for
            adiabatic-connection dissipation fluctuation formula.
        alpha: float
            XXX describe
        gamma_point: bool
            0: Skip k2-k1=0 interactions.
            1: Use the alpha method.
            2: Integrate the gamma point.
        bandstructure: bool
            Calculate bandstructure instead of just the total energy.
        bands: list of int
            List of bands to calculate bandstructure for.  Default is
            all bands.
        molecule: bool
            Decouple electrostatic interactions between periodically
            repeated images.
        fcut: float
            Threshold for empty band.
        """

        self.alpha = alpha
        self.fcut = fcut

        self.gamma_point = gamma_point
        self.method = method
        self.bandstructure = bandstructure
        self.bands = bands

        self.fd = logfilename
        self.write_timing_information = True

        HybridXCBase.__init__(self, name, hybrid, xc)

        # EXX energies:
        self.exx = None  # total
        self.evv = None  # valence-valence (pseudo part)
        self.evvacdf = None  # valence-valence (pseudo part)
        self.devv = None  # valence-valence (PAW correction)
        self.evc = None  # valence-core
        self.ecc = None  # core-core

        self.exx_skn = None  # bandstructure

        self.qlatest = None

        if world is None:
            world = mpi.world
        self.world = world

        self.molecule = molecule
        
        if isinstance(qstride, int):
            qstride = [qstride] * 3
        self.qstride_c = np.asarray(qstride)
        
        self.timer = Timer()

    def log(self, *args, **kwargs):
        prnt(file=self.fd, *args, **kwargs)
        self.fd.flush()

    def calculate_radial(self, rgd, n_sLg, Y_L, v_sg,
                         dndr_sLg=None, rnablaY_Lv=None,
                         tau_sg=None, dedtau_sg=None):
        return self.xc.calculate_radial(rgd, n_sLg, Y_L, v_sg,
                                        dndr_sLg, rnablaY_Lv)
    
    def calculate_paw_correction(self, setup, D_sp, dEdD_sp=None,
                                 addcoredensity=True, a=None):
        return self.xc.calculate_paw_correction(setup, D_sp, dEdD_sp,
                                 addcoredensity, a)
    
    def initialize(self, dens, ham, wfs, occupations):
        assert wfs.bd.comm.size == 1

        self.xc.initialize(dens, ham, wfs, occupations)

        self.dens = dens
        self.wfs = wfs

        # Make a k-point descriptor that is not distributed
        # (self.kd.comm is serial_comm):
        self.kd = wfs.kd.copy()

        self.fd = logfile(self.fd, self.world.rank)

        wfs.initialize_wave_functions_from_restart_file()

    def set_positions(self, spos_ac):
        self.spos_ac = spos_ac

    def calculate(self, gd, n_sg, v_sg=None, e_g=None):
        # Normal XC contribution:
        exc = self.xc.calculate(gd, n_sg, v_sg, e_g)

        # Add EXX contribution:
        return exc + self.exx * self.hybrid

    def calculate_exx(self):
        """Non-selfconsistent calculation."""

        self.timer.start('EXX')
        self.timer.start('Initialization')
        
        kd = self.kd
        wfs = self.wfs

        if fftw.FFTPlan is fftw.NumpyFFTPlan:
            self.log('NOT USING FFTW !!')

        self.log('Spins:', self.wfs.nspins)

        W = max(1, self.wfs.kd.comm.size // self.wfs.nspins)
        # Are the k-points distributed?
        kparallel = (W > 1)

        # Find number of occupied bands:
        self.nocc_sk = np.zeros((self.wfs.nspins, kd.nibzkpts), int)
        for kpt in self.wfs.kpt_u:
            for n, f in enumerate(kpt.f_n):
                if abs(f) < self.fcut:
                    self.nocc_sk[kpt.s, kpt.k] = n
                    break
            else:
                self.nocc_sk[kpt.s, kpt.k] = self.wfs.bd.nbands
        self.wfs.kd.comm.sum(self.nocc_sk)

        noccmin = self.nocc_sk.min()
        noccmax = self.nocc_sk.max()
        self.log('Number of occupied bands (min, max): %d, %d' %
                 (noccmin, noccmax))
        
        self.log('Number of valence electrons:', self.wfs.setups.nvalence)

        if self.bandstructure:
            self.log('Calculating eigenvalue shifts.')

            # allocate array for eigenvalue shifts:
            self.exx_skn = np.zeros((self.wfs.nspins,
                                     kd.nibzkpts,
                                     self.wfs.bd.nbands))

            if self.bands is None:
                noccmax = self.wfs.bd.nbands
            else:
                noccmax = max(max(self.bands) + 1, noccmax)

        N_c = self.kd.N_c

        vol = wfs.gd.dv * wfs.gd.N_c.prod()
        if self.alpha is None:
            alpha = 6 * vol**(2 / 3.0) / pi**2
        else:
            alpha = self.alpha
        if self.gamma_point == 1:
            if alpha == 0.0:
                qvol = (2*np.pi)**3 / vol / N_c.prod()
                self.gamma = 4*np.pi * (3*qvol / (4*np.pi))**(1/3.) / qvol
            else:
                self.gamma = self.calculate_gamma(vol, alpha)
        else:
            kcell_cv = wfs.gd.cell_cv.copy()
            kcell_cv[0] *= N_c[0]
            kcell_cv[1] *= N_c[1]
            kcell_cv[2] *= N_c[2]
            self.gamma = madelung(kcell_cv) * vol * N_c.prod() / (4 * np.pi)

        self.log('Value of alpha parameter: %.3f Bohr^2' % alpha)
        self.log('Value of gamma parameter: %.3f Bohr^2' % self.gamma)
            
        # Construct all possible q=k2-k1 vectors:
        Nq_c = (N_c - 1) // self.qstride_c
        i_qc = np.indices(Nq_c * 2 + 1, float).transpose(
            (1, 2, 3, 0)).reshape((-1, 3))
        self.bzq_qc = (i_qc - Nq_c) / N_c * self.qstride_c
        self.q0 = ((Nq_c * 2 + 1).prod() - 1) // 2  # index of q=(0,0,0)
        assert not self.bzq_qc[self.q0].any()

        # Count number of pairs for each q-vector:
        self.npairs_q = np.zeros(len(self.bzq_qc), int)
        for s in range(kd.nspins):
            for k1 in range(kd.nibzkpts):
                for k2 in range(kd.nibzkpts):
                    for K2, q, n1_n, n2 in self.indices(s, k1, k2):
                        self.npairs_q[q] += len(n1_n)

        self.npairs0 = self.npairs_q.sum()  # total number of pairs

        self.log('Number of pairs:', self.npairs0)

        # Distribute q-vectors to Q processors:
        Q = self.world.size // self.wfs.kd.comm.size
        myrank = self.world.rank // self.wfs.kd.comm.size
        rank = 0
        N = 0
        myq = []
        nq = 0
        for q, n in enumerate(self.npairs_q):
            if n > 0:
                nq += 1
                if rank == myrank:
                    myq.append(q)
            N += n
            if N >= (rank + 1.0) * self.npairs0 / Q:
                rank += 1

        assert len(myq) > 0, 'Too few q-vectors for too many processes!'
        self.bzq_qc = self.bzq_qc[myq]
        try:
            self.q0 = myq.index(self.q0)
        except ValueError:
            self.q0 = None

        self.log('%d x %d x %d k-points' % tuple(self.kd.N_c))
        self.log('Distributing %d IBZ k-points over %d process(es).' %
                 (kd.nibzkpts, self.wfs.kd.comm.size))
        self.log('Distributing %d q-vectors over %d process(es).' % (nq, Q))

        # q-point descriptor for my q-vectors:
        qd = KPointDescriptor(self.bzq_qc)

        # Plane-wave descriptor for all wave-functions:
        self.pd = PWDescriptor(wfs.pd.ecut, wfs.gd,
                               dtype=wfs.pd.dtype, kd=kd)

        # Plane-wave descriptor pair-densities:
        self.pd2 = PWDescriptor(self.dens.pd2.ecut, self.dens.gd,
                                dtype=wfs.dtype, kd=qd)

        self.log('Cutoff energies:')
        self.log('    Wave functions:       %10.3f eV' %
                 (self.pd.ecut * Hartree))
        self.log('    Density:              %10.3f eV' %
                 (self.pd2.ecut * Hartree))

        # Calculate 1/|G+q|^2 with special treatment of |G+q|=0:
        G2_qG = self.pd2.G2_qG
        if self.q0 is None:
            if self.omega is None:
                self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG]
            else:
                self.iG2_qG = [(1.0 / G2_G *
                                (1 - np.exp(-G2_G / (4 * self.omega**2))))
                               for G2_G in G2_qG]
        else:
            G2_qG[self.q0][0] = 117.0  # avoid division by zero
            if self.omega is None:
                self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG]
                self.iG2_qG[self.q0][0] = self.gamma
            else:
                self.iG2_qG = [(1.0 / G2_G *
                                (1 - np.exp(-G2_G / (4 * self.omega**2))))
                               for G2_G in G2_qG]
                self.iG2_qG[self.q0][0] = 1 / (4 * self.omega**2)
            G2_qG[self.q0][0] = 0.0  # restore correct value

        # Compensation charges:
        self.ghat = PWLFC([setup.ghat_l for setup in wfs.setups], self.pd2)
        self.ghat.set_positions(self.spos_ac)

        if self.molecule:
            self.initialize_gaussian()
            self.log('Value of beta parameter: %.3f 1/Bohr^2' % self.beta)
            
        self.timer.stop('Initialization')
        
        # Ready ... set ... go:
        self.t0 = time()
        self.npairs = 0
        self.evv = 0.0
        self.evvacdf = 0.0
        for s in range(self.wfs.nspins):
            kpt1_q = [KPoint(self.wfs, noccmax).initialize(kpt)
                      for kpt in self.wfs.kpt_u if kpt.s == s]
            kpt2_q = kpt1_q[:]

            if len(kpt1_q) == 0:
                # No s-spins on this CPU:
                continue

            # Send and receive ranks:
            srank = self.wfs.kd.get_rank_and_index(
                s, (kpt1_q[0].k - 1) % kd.nibzkpts)[0]
            rrank = self.wfs.kd.get_rank_and_index(
                s, (kpt1_q[-1].k + 1) % kd.nibzkpts)[0]

            # Shift k-points kd.nibzkpts - 1 times:
            for i in range(kd.nibzkpts):
                if i < kd.nibzkpts - 1:
                    if kparallel:
                        kpt = kpt2_q[-1].next(self.wfs)
                        kpt.start_receiving(rrank)
                        kpt2_q[0].start_sending(srank)
                    else:
                        kpt = kpt2_q[0]

                self.timer.start('Calculate')
                for kpt1, kpt2 in zip(kpt1_q, kpt2_q):
                    # Loop over all k-points that k2 can be mapped to:
                    for K2, q, n1_n, n2 in self.indices(s, kpt1.k, kpt2.k):
                        self.apply(K2, q, kpt1, kpt2, n1_n, n2)
                self.timer.stop('Calculate')

                if i < kd.nibzkpts - 1:
                    self.timer.start('Wait')
                    if kparallel:
                        kpt.wait()
                        kpt2_q[0].wait()
                    self.timer.stop('Wait')
                    kpt2_q.pop(0)
                    kpt2_q.append(kpt)

        self.evv = self.world.sum(self.evv)
        self.evvacdf = self.world.sum(self.evvacdf)
        self.calculate_exx_paw_correction()
        
        if self.method == 'standard':
            self.exx = self.evv + self.devv + self.evc + self.ecc
        elif self.method == 'acdf':
            self.exx = self.evvacdf + self.devv + self.evc + self.ecc
        else:
            1 / 0

        self.log('Exact exchange energy:')
        for txt, e in [
            ('core-core', self.ecc),
            ('valence-core', self.evc),
            ('valence-valence (pseudo, acdf)', self.evvacdf),
            ('valence-valence (pseudo, standard)', self.evv),
            ('valence-valence (correction)', self.devv),
            ('total (%s)' % self.method, self.exx)]:
            self.log('    %-36s %14.6f eV' % (txt + ':', e * Hartree))

        self.log('Total time: %10.3f seconds' % (time() - self.t0))

        self.npairs = self.world.sum(self.npairs)
        assert self.npairs == self.npairs0
        
        self.timer.stop('EXX')
        self.timer.write(self.fd)

    def calculate_gamma(self, vol, alpha):
        if self.molecule:
            return 0.0

        N_c = self.kd.N_c
        offset_c = (N_c + 1) % 2 * 0.5 / N_c
        bzq_qc = monkhorst_pack(N_c) + offset_c
        qd = KPointDescriptor(bzq_qc)
        pd = PWDescriptor(self.wfs.pd.ecut, self.wfs.gd, kd=qd)
        gamma = (vol / (2 * pi)**2 * sqrt(pi / alpha) *
                 self.kd.nbzkpts)
        for G2_G in pd.G2_qG:
            if G2_G[0] < 1e-7:
                G2_G = G2_G[1:]
            gamma -= np.dot(np.exp(-alpha * G2_G), G2_G**-1)
        return gamma / self.qstride_c.prod()

    def indices(self, s, k1, k2):
        """Generator for (K2, q, n1, n2) indices for (k1, k2) pair.

        s: int
            Spin index.
        k1: int
            Index of k-point in the IBZ.
        k2: int
            Index of k-point in the IBZ.

        Returns (K, q, n1_n, n2), where K then index of the k-point in
        the BZ that k2 is mapped to, q is the index of the q-vector
        between K and k1, and n1_n is a list of bands that should be
        combined with band n2."""

        for K, k in enumerate(self.kd.bz2ibz_k):
            if k == k2:
                for K, q, n1_n, n2 in self._indices(s, k1, k2, K):
                    yield K, q, n1_n, n2
            
    def _indices(self, s, k1, k2, K2):
        k1_c = self.kd.ibzk_kc[k1]
        k2_c = self.kd.bzk_kc[K2]
        q_c = k2_c - k1_c
        q = abs(self.bzq_qc - q_c).sum(1).argmin()
        if abs(self.bzq_qc[q] - q_c).sum() > 1e-7:
            return

        if self.gamma_point == 0 and q == self.q0:
            return

        nocc1 = self.nocc_sk[s, k1]
        nocc2 = self.nocc_sk[s, k2]

        # Is k2 in the IBZ?
        is_ibz2 = (self.kd.ibz2bz_k[k2] == K2)

        for n2 in range(self.wfs.bd.nbands):
            # Find range of n1's (from n1a to n1b-1):
            if is_ibz2:
                # We get this combination twice, so let's only do half:
                if k1 >= k2:
                    n1a = n2
                else:
                    n1a = n2 + 1
            else:
                n1a = 0

            n1b = self.wfs.bd.nbands

            if self.bandstructure:
                if n2 >= nocc2:
                    n1b = min(n1b, nocc1)
            else:
                if n2 >= nocc2:
                    break
                n1b = min(n1b, nocc1)

            if self.bands is not None:
                assert self.bandstructure
                n1_n = []
                for n1 in range(n1a, n1b):
                    if (n1 in self.bands and n2 < nocc2 or
                        is_ibz2 and n2 in self.bands and n1 < nocc1):
                        n1_n.append(n1)
                n1_n = np.array(n1_n)
            else:
                n1_n = np.arange(n1a, n1b)

            if len(n1_n) == 0:
                continue

            yield K2, q, n1_n, n2

    def apply(self, K2, q, kpt1, kpt2, n1_n, n2):
        k20_c = self.kd.ibzk_kc[kpt2.k]
        k2_c = self.kd.bzk_kc[K2]

        if k2_c.any():
            self.timer.start('Initialize plane waves')
            eik2r_R = self.wfs.gd.plane_wave(k2_c)
            eik20r_R = self.wfs.gd.plane_wave(k20_c)
            self.timer.stop('Initialize plane waves')
        else:
            eik2r_R = 1.0
            eik20r_R = 1.0

        w1 = self.kd.weight_k[kpt1.k]
        w2 = self.kd.weight_k[kpt2.k]

        # Is k2 in the 1. BZ?
        is_ibz2 = (self.kd.ibz2bz_k[kpt2.k] == K2)

        e_n = self.calculate_interaction(n1_n, n2, kpt1, kpt2, q, K2,
                                         eik20r_R, eik2r_R,
                                         is_ibz2)

        e_n *= 1.0 / self.kd.nbzkpts / self.wfs.nspins * self.qstride_c.prod()
        
        if q == self.q0:
            e_n[n1_n == n2] *= 0.5

        f1_n = kpt1.f_n[n1_n]
        eps1_n = kpt1.eps_n[n1_n]
        f2 = kpt2.f_n[n2]
        eps2 = kpt2.eps_n[n2]

        s_n = np.sign(eps2 - eps1_n)

        evv = (f1_n * f2 * e_n).sum()
        evvacdf = 0.5 * (f1_n * (1 - s_n) * e_n +
                         f2 * (1 + s_n) * e_n).sum()
        self.evv += evv * w1
        self.evvacdf += evvacdf * w1
        if is_ibz2:
            self.evv += evv * w2
            self.evvacdf += evvacdf * w2

        if self.bandstructure:
            x = self.wfs.nspins
            self.exx_skn[kpt1.s, kpt1.k, n1_n] += x * f2 * e_n
            if is_ibz2:
                self.exx_skn[kpt2.s, kpt2.k, n2] += x * np.dot(f1_n, e_n)

    def calculate_interaction(self, n1_n, n2, kpt1, kpt2, q, k,
                              eik20r_R, eik2r_R, is_ibz2):
        """Calculate Coulomb interactions.

        For all n1 in the n1_n list, calculate interaction with n2."""

        # number of plane waves:
        ng1 = self.wfs.ng_k[kpt1.k]
        ng2 = self.wfs.ng_k[kpt2.k]

        # Transform to real space and apply symmetry operation:
        self.timer.start('IFFT1')
        if is_ibz2:
            u2_R = self.pd.ifft(kpt2.psit_nG[n2, :ng2], kpt2.k)
        else:
            psit2_R = self.pd.ifft(kpt2.psit_nG[n2, :ng2], kpt2.k) * eik20r_R
            self.timer.start('Symmetry transform')
            u2_R = self.kd.transform_wave_function(psit2_R, k) / eik2r_R
            self.timer.stop()
        self.timer.stop()

        # Calculate pair densities:
        nt_nG = self.pd2.zeros(len(n1_n), q=q)
        for n1, nt_G in zip(n1_n, nt_nG):
            self.timer.start('IFFT2')
            u1_R = self.pd.ifft(kpt1.psit_nG[n1, :ng1], kpt1.k)
            self.timer.stop()
            nt_R = u1_R.conj() * u2_R
            self.timer.start('FFT')
            nt_G[:] = self.pd2.fft(nt_R, q)
            self.timer.stop()
        
        s = self.kd.sym_k[k]
        time_reversal = self.kd.time_reversal_k[k]
        k2_c = self.kd.ibzk_kc[kpt2.k]

        self.timer.start('Compensation charges')
        Q_anL = {}  # coefficients for shape functions
        for a, P1_ni in kpt1.P_ani.items():
            P1_ni = P1_ni[n1_n]

            if is_ibz2:
                P2_i = kpt2.P_ani[a][n2]
            else:
                b = self.kd.symmetry.a_sa[s, a]
                S_c = (np.dot(self.spos_ac[a], self.kd.symmetry.op_scc[s]) -
                       self.spos_ac[b])
                assert abs(S_c.round() - S_c).max() < 1e-5
                if self.ghat.dtype == complex:
                    x = np.exp(2j * pi * np.dot(k2_c, S_c))
                else:
                    x = 1.0
                P2_i = np.dot(self.wfs.setups[a].R_sii[s],
                              kpt2.P_ani[b][n2]) * x
                if time_reversal:
                    P2_i = P2_i.conj()

            D_np = []
            for P1_i in P1_ni:
                D_ii = np.outer(P1_i.conj(), P2_i)
                D_np.append(pack(D_ii))
            Q_anL[a] = np.dot(D_np, self.wfs.setups[a].Delta_pL)
            
        self.timer.start('Expand')
        if q != self.qlatest:
            self.f_IG = self.ghat.expand(q)
            self.qlatest = q
        self.timer.stop('Expand')

        # Add compensation charges:
        self.ghat.add(nt_nG, Q_anL, q, self.f_IG)
        self.timer.stop('Compensation charges')

        if self.molecule and n2 in n1_n:
            nn = (n1_n == n2).nonzero()[0][0]
            nt_nG[nn] -= self.ngauss_G
        else:
            nn = None
            
        iG2_G = self.iG2_qG[q]
        
        # Calculate energies:
        e_n = np.empty(len(n1_n))
        for n, nt_G in enumerate(nt_nG):
            e_n[n] = -4 * pi * np.real(self.pd2.integrate(nt_G, nt_G * iG2_G))
            self.npairs += 1
        
        if nn is not None:
            e_n[nn] -= 2 * (self.pd2.integrate(nt_nG[nn], self.vgauss_G) +
                            (self.beta / 2 / pi)**0.5)

        if self.write_timing_information:
            t = (time() - self.t0) / len(n1_n)
            self.log('Time for first pair-density: %10.3f seconds' % t)
            self.log('Estimated total time:        %10.3f seconds' %
                     (t * self.npairs0 / self.world.size))
            self.write_timing_information = False

        return e_n

    def calculate_exx_paw_correction(self):
        self.timer.start('PAW correction')
        self.devv = 0.0
        self.evc = 0.0
        self.ecc = 0.0
                         
        deg = 2 // self.wfs.nspins  # spin degeneracy
        for a, D_sp in self.dens.D_asp.items():
            setup = self.wfs.setups[a]
            for D_p in D_sp:
                D_ii = unpack2(D_p)
                ni = len(D_ii)

                for i1 in range(ni):
                    for i2 in range(ni):
                        A = 0.0
                        for i3 in range(ni):
                            p13 = packed_index(i1, i3, ni)
                            for i4 in range(ni):
                                p24 = packed_index(i2, i4, ni)
                                A += setup.M_pp[p13, p24] * D_ii[i3, i4]
                        self.devv -= D_ii[i1, i2] * A / deg

                self.evc -= np.dot(D_p, setup.X_p)
            self.ecc += setup.ExxC

        if not self.bandstructure:
            self.timer.stop('PAW correction')
            return

        Q = self.world.size // self.wfs.kd.comm.size
        self.exx_skn *= Q
        for kpt in self.wfs.kpt_u:
            for a, D_sp in self.dens.D_asp.items():
                setup = self.wfs.setups[a]
                for D_p in D_sp:
                    D_ii = unpack2(D_p)
                    ni = len(D_ii)
                    P_ni = kpt.P_ani[a]
                    for i1 in range(ni):
                        for i2 in range(ni):
                            A = 0.0
                            for i3 in range(ni):
                                p13 = packed_index(i1, i3, ni)
                                for i4 in range(ni):
                                    p24 = packed_index(i2, i4, ni)
                                    A += setup.M_pp[p13, p24] * D_ii[i3, i4]
                            self.exx_skn[kpt.s, kpt.k] -= \
                                (A * P_ni[:, i1].conj() * P_ni[:, i2]).real
                            p12 = packed_index(i1, i2, ni)
                            self.exx_skn[kpt.s, kpt.k] -= \
                                (P_ni[:, i1].conj() * setup.X_p[p12] *
                                 P_ni[:, i2]).real / self.wfs.nspins

        self.world.sum(self.exx_skn)
        self.exx_skn *= self.hybrid / Q
        self.timer.stop('PAW correction')
    
    def initialize_gaussian(self):
        """Calculate gaussian compensation charge and its potential.

        Used to decouple electrostatic interactions between
        periodically repeated images for molecular calculations.

        Charge containing one electron::

            (beta/pi)^(3/2)*exp(-beta*r^2),

        its Fourier transform::

            exp(-G^2/(4*beta)),

        and its potential::

            erf(beta^0.5*r)/r.
        """

        gd = self.wfs.gd

        # Set exponent of exp-function to -19 on the boundary:
        self.beta = 4 * 19 * (gd.icell_cv**2).sum(1).max()

        # Calculate gaussian:
        G_Gv = self.pd2.G_Qv[self.pd2.Q_qG[0]]
        G2_G = self.pd2.G2_qG[0]
        C_v = gd.cell_cv.sum(0) / 2  # center of cell
        self.ngauss_G = np.exp(-1.0 / (4 * self.beta) * G2_G +
                                1j * np.dot(G_Gv, C_v)) / gd.dv

        # Calculate potential from gaussian:
        R_Rv = gd.get_grid_point_coordinates().transpose((1, 2, 3, 0))
        r_R = ((R_Rv - C_v)**2).sum(3)**0.5
        if (gd.N_c % 2 == 0).all():
            r_R[tuple(gd.N_c // 2)] = 1.0  # avoid dividing by zero
        v_R = erf(self.beta**0.5 * r_R) / r_R
        if (gd.N_c % 2 == 0).all():
            v_R[tuple(gd.N_c // 2)] = (4 * self.beta / pi)**0.5
        self.vgauss_G = self.pd2.fft(v_R)

        # Compare self-interaction to analytic result:
        assert abs(0.5 * self.pd2.integrate(self.ngauss_G, self.vgauss_G) -
                   (self.beta / 2 / pi)**0.5) < 1e-6
Example #12
0
    def get_rpa(self):
        """calculate RPA part of the omega matrix"""

        # shorthands
        kss=self.fullkss
        finegrid=self.finegrid
        wfs = self.paw.wfs
        eh_comm = self.eh_comm
        
        # calculate omega matrix
        nij = len(kss)
        print >> self.txt,'RPA',nij,'transitions'
        
        Om = self.Om
        
        for ij in range(eh_comm.rank, nij, eh_comm.size):
            print >> self.txt,'RPA kss['+'%d'%ij+']=', kss[ij]

            timer = Timer()
            timer.start('init')
            timer2 = Timer()
                      
            # smooth density including compensation charges
            timer2.start('with_compensation_charges 0')
            rhot_p = kss[ij].with_compensation_charges(
                finegrid is not 0)
            timer2.stop()
            
            # integrate with 1/|r_1-r_2|
            timer2.start('poisson')
            phit_p = np.zeros(rhot_p.shape, rhot_p.dtype.char)
            self.poisson.solve(phit_p, rhot_p, charge=None)
            timer2.stop()

            timer.stop()
            t0 = timer.get_time('init')
            timer.start(ij)

            if finegrid == 1:
                rhot = kss[ij].with_compensation_charges()
                phit = self.gd.zeros()
##                print "shapes 0=",phit.shape,rhot.shape
                self.restrict(phit_p,phit)
            else:
                phit = phit_p
                rhot = rhot_p

            for kq in range(ij,nij):
                if kq != ij:
                    # smooth density including compensation charges
                    timer2.start('kq with_compensation_charges')
                    rhot = kss[kq].with_compensation_charges(
                        finegrid is 2)
                    timer2.stop()

                timer2.start('integrate')
                pre = 2.*sqrt(kss[ij].get_energy()*kss[kq].get_energy()*
                                  kss[ij].get_weight()*kss[kq].get_weight())
                Om[ij,kq]= pre * self.gd.integrate(rhot*phit)
##                print "int=",Om[ij,kq]
                timer2.stop()

                # Add atomic corrections
                timer2.start('integrate corrections 2')
                Ia = 0.
                for a, P_ni in wfs.kpt_u[kss[ij].spin].P_ani.items():
                    Pi_i = P_ni[kss[ij].i]
                    Pj_i = P_ni[kss[ij].j]
                    Dij_ii = np.outer(Pi_i, Pj_i)
                    Dij_p = pack(Dij_ii)
                    Pkq_ni = wfs.kpt_u[kss[kq].spin].P_ani[a]
                    Pk_i = Pkq_ni[kss[kq].i]
                    Pq_i = Pkq_ni[kss[kq].j]
                    Dkq_ii = np.outer(Pk_i, Pq_i)
                    Dkq_p = pack(Dkq_ii)
                    C_pp = wfs.setups[a].M_pp
                    #   ----
                    # 2 >      P   P  C    P  P
                    #   ----    ip  jr prst ks qt
                    #   prst
                    Ia += 2.0*np.dot(Dkq_p,np.dot(C_pp,Dij_p))
                timer2.stop()
                
                Om[ij,kq] += pre * self.gd.comm.sum(Ia)
                    
                if ij == kq:
                    Om[ij,kq] += kss[ij].get_energy()**2
                else:
                    Om[kq,ij]=Om[ij,kq]

            timer.stop()
##            timer2.write()
            if ij < (nij-1):
                t = timer.get_time(ij) # time for nij-ij calculations
                t = .5*t*(nij-ij)  # estimated time for n*(n+1)/2, n=nij-(ij+1)
                print >> self.txt,'RPA estimated time left',\
                      self.timestring(t0*(nij-ij-1)+t)
Example #13
0
    def get_rpa(self):
        """Calculate RPA and Hartree-fock part of the A+-B matrices."""

        # shorthands
        kss = self.fullkss
        finegrid = self.finegrid

        # calculate omega matrix
        nij = len(kss)
        print >> self.txt, 'RPAhyb', nij, 'transitions'
        
        AmB = np.zeros((nij, nij))
        ApB = self.ApB

        # storage place for Coulomb integrals
        integrals = {}
        
        for ij in range(nij):
            print >> self.txt,'RPAhyb kss['+'%d'%ij+']=', kss[ij]

            timer = Timer()
            timer.start('init')
            timer2 = Timer()
                      
            # smooth density including compensation charges
            timer2.start('with_compensation_charges 0')
            rhot_p = kss[ij].with_compensation_charges(
                finegrid is not 0)
            timer2.stop()
            
            # integrate with 1/|r_1-r_2|
            timer2.start('poisson')
            phit_p = np.zeros(rhot_p.shape, rhot_p.dtype)
            self.poisson.solve(phit_p,rhot_p, charge=None)
            timer2.stop()

            timer.stop()
            t0 = timer.get_time('init')
            timer.start(ij)

            if finegrid == 1:
                rhot = kss[ij].with_compensation_charges()
                phit = self.gd.zeros()
                self.restrict(phit_p, phit)
            else:
                phit = phit_p
                rhot = rhot_p

            for kq in range(ij, nij):
                if kq != ij:
                    # smooth density including compensation charges
                    timer2.start('kq with_compensation_charges')
                    rhot = kss[kq].with_compensation_charges(
                        finegrid is 2)
                    timer2.stop()
                pre = self.weight_Kijkq(ij, kq)

                timer2.start('integrate')
                I = self.Coulomb_integral_kss(kss[ij], kss[kq], phit, rhot)
                if kss[ij].spin == kss[kq].spin:
                    name = self.Coulomb_integral_name(kss[ij].i, kss[ij].j,
                                                      kss[kq].i, kss[kq].j,
                                                      kss[ij].spin         )
                    integrals[name] = I
                ApB[ij,kq]= pre * I
                timer2.stop()
                
                if ij == kq:
                    epsij =  kss[ij].get_energy() / kss[ij].get_weight()
                    AmB[ij,kq] += epsij
                    ApB[ij,kq] += epsij

            timer.stop()
##            timer2.write()
            if ij < (nij - 1):
                t = timer.get_time(ij) # time for nij-ij calculations
                t = .5*t*(nij-ij)  # estimated time for n*(n+1)/2, n=nij-(ij+1)
                print >> self.txt,'RPAhyb estimated time left',\
                      self.timestring(t0*(nij-ij-1)+t)

        # add HF parts and apply symmetry
        timer.start('RPA hyb HF part')
        if hasattr(self.xc, 'hybrid'):
            weight = self.xc.hybrid
        else:
            weight = 0.0
        for ij in range(nij):
            i = kss[ij].i
            j = kss[ij].j
            s = kss[ij].spin
            for kq in range(ij,nij):
                if kss[ij].pspin == kss[kq].pspin:
                    k = kss[kq].i
                    q = kss[kq].j
                    ikjq = self.Coulomb_integral_ijkq(i, k, j, q, s, integrals)
                    iqkj = self.Coulomb_integral_ijkq(i, q, k, j, s, integrals)
                    ApB[ij,kq] -= weight * ( ikjq + iqkj )
                    AmB[ij,kq] -= weight * ( ikjq - iqkj )
                
                ApB[kq,ij] = ApB[ij,kq]
                AmB[kq,ij] = AmB[ij,kq]
        timer.stop()
        
        return AmB
Example #14
0
    def get_rpa(self):
        """calculate RPA part of the omega matrix"""

        # shorthands
        kss = self.fullkss
        finegrid = self.finegrid
        wfs = self.paw.wfs
        eh_comm = self.eh_comm

        # calculate omega matrix
        nij = len(kss)
        print >> self.txt, 'RPA', nij, 'transitions'

        Om = self.Om

        for ij in range(eh_comm.rank, nij, eh_comm.size):
            print >> self.txt, 'RPA kss[' + '%d' % ij + ']=', kss[ij]

            timer = Timer()
            timer.start('init')
            timer2 = Timer()

            # smooth density including compensation charges
            timer2.start('with_compensation_charges 0')
            rhot_p = kss[ij].with_compensation_charges(finegrid is not 0)
            timer2.stop()

            # integrate with 1/|r_1-r_2|
            timer2.start('poisson')
            phit_p = np.zeros(rhot_p.shape, rhot_p.dtype.char)
            self.poisson.solve(phit_p, rhot_p, charge=None)
            timer2.stop()

            timer.stop()
            t0 = timer.get_time('init')
            timer.start(ij)

            if finegrid == 1:
                rhot = kss[ij].with_compensation_charges()
                phit = self.gd.zeros()
                ##                print "shapes 0=",phit.shape,rhot.shape
                self.restrict(phit_p, phit)
            else:
                phit = phit_p
                rhot = rhot_p

            for kq in range(ij, nij):
                if kq != ij:
                    # smooth density including compensation charges
                    timer2.start('kq with_compensation_charges')
                    rhot = kss[kq].with_compensation_charges(finegrid is 2)
                    timer2.stop()

                timer2.start('integrate')
                pre = 2. * sqrt(kss[ij].get_energy() * kss[kq].get_energy() *
                                kss[ij].get_weight() * kss[kq].get_weight())
                Om[ij, kq] = pre * self.gd.integrate(rhot * phit)
                ##                print "int=",Om[ij,kq]
                timer2.stop()

                # Add atomic corrections
                timer2.start('integrate corrections 2')
                Ia = 0.
                for a, P_ni in wfs.kpt_u[kss[ij].spin].P_ani.items():
                    Pi_i = P_ni[kss[ij].i]
                    Pj_i = P_ni[kss[ij].j]
                    Dij_ii = np.outer(Pi_i, Pj_i)
                    Dij_p = pack(Dij_ii)
                    Pkq_ni = wfs.kpt_u[kss[kq].spin].P_ani[a]
                    Pk_i = Pkq_ni[kss[kq].i]
                    Pq_i = Pkq_ni[kss[kq].j]
                    Dkq_ii = np.outer(Pk_i, Pq_i)
                    Dkq_p = pack(Dkq_ii)
                    C_pp = wfs.setups[a].M_pp
                    #   ----
                    # 2 >      P   P  C    P  P
                    #   ----    ip  jr prst ks qt
                    #   prst
                    Ia += 2.0 * np.dot(Dkq_p, np.dot(C_pp, Dij_p))
                timer2.stop()

                Om[ij, kq] += pre * self.gd.comm.sum(Ia)

                if ij == kq:
                    Om[ij, kq] += kss[ij].get_energy()**2
                else:
                    Om[kq, ij] = Om[ij, kq]

            timer.stop()
            ##            timer2.write()
            if ij < (nij - 1):
                t = timer.get_time(ij)  # time for nij-ij calculations
                t = .5 * t * (nij - ij
                              )  # estimated time for n*(n+1)/2, n=nij-(ij+1)
                print >> self.txt,'RPA estimated time left',\
                      self.timestring(t0*(nij-ij-1)+t)
Example #15
0
    def get_xc(self):
        """Add xc part of the coupling matrix"""

        # shorthands
        paw = self.paw
        wfs = paw.wfs
        gd = paw.density.finegd
        comm = gd.comm
        eh_comm = self.eh_comm

        fg = self.finegrid is 2
        kss = self.fullkss
        nij = len(kss)

        Om_xc = self.Om
        # initialize densities
        # nt_sg is the smooth density on the fine grid with spin index

        if kss.nvspins == 2:
            # spin polarised ground state calc.
            nt_sg = paw.density.nt_sg
        else:
            # spin unpolarised ground state calc.
            if kss.npspins == 2:
                # construct spin polarised densities
                nt_sg = np.array(
                    [.5 * paw.density.nt_sg[0], .5 * paw.density.nt_sg[0]])
            else:
                nt_sg = paw.density.nt_sg
        # check if D_sp have been changed before
        D_asp = self.paw.density.D_asp
        for a, D_sp in D_asp.items():
            if len(D_sp) != kss.npspins:
                if len(D_sp) == 1:
                    D_asp[a] = np.array([0.5 * D_sp[0], 0.5 * D_sp[0]])
                else:
                    D_asp[a] = np.array([D_sp[0] + D_sp[1]])

        # restrict the density if needed
        if fg:
            nt_s = nt_sg
        else:
            nt_s = self.gd.zeros(nt_sg.shape[0])
            for s in range(nt_sg.shape[0]):
                self.restrict(nt_sg[s], nt_s[s])
            gd = paw.density.gd

        # initialize vxc or fxc

        if self.derivativeLevel == 0:
            raise NotImplementedError
            if kss.npspins == 2:
                v_g = nt_sg[0].copy()
            else:
                v_g = nt_sg.copy()
        elif self.derivativeLevel == 1:
            pass
        elif self.derivativeLevel == 2:
            raise NotImplementedError
            if kss.npspins == 2:
                fxc = d2Excdnsdnt(nt_sg[0], nt_sg[1])
            else:
                fxc = d2Excdn2(nt_sg)
        else:
            raise ValueError('derivativeLevel can only be 0,1,2')

##        self.paw.my_nuclei = []

        ns = self.numscale
        xc = self.xc
        print >> self.txt, 'XC', nij, 'transitions'
        for ij in range(eh_comm.rank, nij, eh_comm.size):
            print >> self.txt, 'XC kss[' + '%d' % ij + ']'

            timer = Timer()
            timer.start('init')
            timer2 = Timer()

            if self.derivativeLevel == 1:
                # vxc is available
                # We use the numerical two point formula for calculating
                # the integral over fxc*n_ij. The results are
                # vvt_s        smooth integral
                # nucleus.I_sp atom based correction matrices (pack2)
                #              stored on each nucleus
                timer2.start('init v grids')
                vp_s = np.zeros(nt_s.shape, nt_s.dtype.char)
                vm_s = np.zeros(nt_s.shape, nt_s.dtype.char)
                if kss.npspins == 2:  # spin polarised
                    nv_s = nt_s.copy()
                    nv_s[kss[ij].pspin] += ns * kss[ij].get(fg)
                    xc.calculate(gd, nv_s, vp_s)
                    nv_s = nt_s.copy()
                    nv_s[kss[ij].pspin] -= ns * kss[ij].get(fg)
                    xc.calculate(gd, nv_s, vm_s)
                else:  # spin unpolarised
                    nv = nt_s + ns * kss[ij].get(fg)
                    xc.calculate(gd, nv, vp_s)
                    nv = nt_s - ns * kss[ij].get(fg)
                    xc.calculate(gd, nv, vm_s)
                vvt_s = (0.5 / ns) * (vp_s - vm_s)
                timer2.stop()

                # initialize the correction matrices
                timer2.start('init v corrections')
                I_asp = {}
                for a, P_ni in wfs.kpt_u[kss[ij].spin].P_ani.items():
                    # create the modified density matrix
                    Pi_i = P_ni[kss[ij].i]
                    Pj_i = P_ni[kss[ij].j]
                    P_ii = np.outer(Pi_i, Pj_i)
                    # we need the symmetric form, hence we can pack
                    P_p = pack(P_ii)
                    D_sp = self.paw.density.D_asp[a].copy()
                    D_sp[kss[ij].pspin] -= ns * P_p
                    setup = wfs.setups[a]
                    I_sp = np.zeros_like(D_sp)
                    setup.xc_correction.calculate(self.xc, D_sp, I_sp)
                    I_sp *= -1.0
                    D_sp = self.paw.density.D_asp[a].copy()
                    D_sp[kss[ij].pspin] += ns * P_p
                    setup.xc_correction.calculate(self.xc, D_sp, I_sp)
                    I_sp /= 2.0 * ns
                    I_asp[a] = I_sp
                timer2.stop()

            timer.stop()
            t0 = timer.get_time('init')
            timer.start(ij)

            for kq in range(ij, nij):
                weight = self.weight_Kijkq(ij, kq)

                if self.derivativeLevel == 0:
                    # only Exc is available

                    if kss.npspins == 2:  # spin polarised
                        nv_g = nt_sg.copy()
                        nv_g[kss[ij].pspin] +=\
                                        kss[ij].get(fg)
                        nv_g[kss[kq].pspin] +=\
                                        kss[kq].get(fg)
                        Excpp = xc.get_energy_and_potential(\
                                        nv_g[0],v_g,nv_g[1],v_g)
                        nv_g = nt_sg.copy()
                        nv_g[kss[ij].pspin] +=\
                                        kss[ij].get(fg)
                        nv_g[kss[kq].pspin] -= \
                                        kss[kq].get(fg)
                        Excpm = xc.get_energy_and_potential(\
                                            nv_g[0],v_g,nv_g[1],v_g)
                        nv_g = nt_sg.copy()
                        nv_g[kss[ij].pspin] -=\
                                        kss[ij].get(fg)
                        nv_g[kss[kq].pspin] +=\
                                        kss[kq].get(fg)
                        Excmp = xc.get_energy_and_potential(\
                                            nv_g[0],v_g,nv_g[1],v_g)
                        nv_g = nt_sg.copy()
                        nv_g[kss[ij].pspin] -= \
                                        kss[ij].get(fg)
                        nv_g[kss[kq].pspin] -=\
                                        kss[kq].get(fg)
                        Excpp = xc.get_energy_and_potential(\
                                            nv_g[0],v_g,nv_g[1],v_g)
                    else:  # spin unpolarised
                        nv_g=nt_sg + ns*kss[ij].get(fg)\
                              + ns*kss[kq].get(fg)
                        Excpp = xc.get_energy_and_potential(nv_g, v_g)
                        nv_g=nt_sg + ns*kss[ij].get(fg)\
                              - ns*kss[kq].get(fg)
                        Excpm = xc.get_energy_and_potential(nv_g, v_g)
                        nv_g=nt_sg - ns*kss[ij].get(fg)\
                              + ns*kss[kq].get(fg)
                        Excmp = xc.get_energy_and_potential(nv_g, v_g)
                        nv_g=nt_sg - ns*kss[ij].get(fg)\
                              - ns*kss[kq].get(fg)
                        Excmm = xc.get_energy_and_potential(nv_g, v_g)

                    Om_xc[ij,kq] += weight *\
                                0.25*(Excpp-Excmp-Excpm+Excmm)/(ns*ns)

                elif self.derivativeLevel == 1:
                    # vxc is available

                    timer2.start('integrate')
                    Om_xc[ij,kq] += weight*\
                                 self.gd.integrate(kss[kq].get(fg)*
                                                   vvt_s[kss[kq].pspin])
                    timer2.stop()

                    timer2.start('integrate corrections')
                    Exc = 0.
                    for a, P_ni in wfs.kpt_u[kss[kq].spin].P_ani.items():
                        # create the modified density matrix
                        Pk_i = P_ni[kss[kq].i]
                        Pq_i = P_ni[kss[kq].j]
                        P_ii = np.outer(Pk_i, Pq_i)
                        # we need the symmetric form, hence we can pack
                        # use pack as I_sp used pack2
                        P_p = pack(P_ii)
                        Exc += np.dot(I_asp[a][kss[kq].pspin], P_p)
                    Om_xc[ij, kq] += weight * self.gd.comm.sum(Exc)
                    timer2.stop()

                elif self.derivativeLevel == 2:
                    # fxc is available
                    if kss.npspins == 2:  # spin polarised
                        Om_xc[ij,kq] += weight *\
                            gd.integrate(kss[ij].get(fg)*
                                         kss[kq].get(fg)*
                                         fxc[kss[ij].pspin,kss[kq].pspin])
                    else:  # spin unpolarised
                        Om_xc[ij,kq] += weight *\
                            gd.integrate(kss[ij].get(fg)*
                                         kss[kq].get(fg)*
                                         fxc)
                if ij != kq:
                    Om_xc[kq, ij] = Om_xc[ij, kq]

            timer.stop()
            ##            timer2.write()
            if ij < (nij - 1):
                t = timer.get_time(ij)  # time for nij-ij calculations
                t = .5 * t * (nij - ij
                              )  # estimated time for n*(n+1)/2, n=nij-(ij+1)
                print >> self.txt,'XC estimated time left',\
                      self.timestring(t0*(nij-ij-1)+t)
Example #16
0
class DipoleQuad:

    """ Class to handle external potential due to 
        dipole and quadrupole from SCME type 
        calculator object.

    """

    def __init__(self, mm, qm,  mp, calcmm, dyn=False, g=0.5):

        self.mm     = mm     # mm atoms object
        self.mp     = mp     # no. atoms per center of mass (cm)
        self.dyn    = dyn    # dyn. update of potential?
        self.calcmm = calcmm # mm calculator (SCME)
        self.qm     = qm     # qm atoms object
        #
        self.qmidx  = len(qm)# no. QM atoms
        #
        self.nm     = len(self.mm) / self.mp
        self.cm     = self.get_cm(self.nm)
        self.timer  = Timer()
        # smoothing value
        self.g = g

        # initialization
        self.initial = True
        
        # Hold on to old arrays
        self.dipoles = None
        self.qpoles  = None
        self.dipoles_1 = None
        self.qpoles_1  = None
        

    def get_potential(self, gd=None, density=None, 
                      setups=None, nspins=None):
        """ Create external potential from dipoles
            and quadrupoles with origin at the 
            center of mass of each classical 
            molecule in the atoms object """

        if self.initial:
            self.update_potential(gd=gd, density=density, 
                                  setups=setups, nspins=nspins)
            return self.potential    
        elif self.dyn:
            if not self.check_convergence():
                self.update_potential(gd=gd, density=density,
                                      setups=setups, nspins=nspins)
            return self.potential
        else:
            if hasattr(self, 'potential'):
                if gd == self.gd or gd is None:
                # Nothing changed
                    return self.potential


    def update_potential(self, gd=None, density=None, 
                         setups=None, nspins=None):

        # Save old dipoles
        if self.dipoles is not None:
            self.dipoles_1 = self.dipoles.copy()
            self.qpoles_1  = self.qpoles.copy()

        self.gd = gd

        # Grab electric field and derivative values
        self.timer.start('Electric Field and Derivative')
        eF, deF = self.get_efield(density, setups, nspins)
        self.timer.stop('Electric Field and Derivative')

        calcmm = self.calcmm
        mm = self.mm

        #######################################
        # Pass (new) values to SCME
        calcmm.eF  = eF   # <-- ! CHECK
        calcmm.deF = deF
        self.timer.start('SCME Calculation')
        calcmm.calculate(mm)
        self.timer.stop('SCME Calculation')
        #######################################

        # Values are in atomic-units
        dipole = calcmm.dipoles / Bohr
        qpoles = calcmm.qpoles  / Bohr**2

        #
        if rank == MASTER:
            print 'dipoles'
            print dipole * Bohr / Debye
            print 'qpoles'
            print qpoles * Bohr**2 / Debye

        # No. solvent mols
        n = len(self.mm) / self.mp

        # Make empty POT
        potential = np.zeros(gd.end_c-gd.beg_c)

        sG = (np.indices(gd.n_c, float).T + \
              gd.beg_c) / gd.N_c

        # Dipole is something with Bohr**2

        # Place external potential due to Dipoles and Quads on grid
        # For a given MM mol, get all distances
        for a in range(n):
            nsG = sG - np.linalg.solve(gd.cell_cv.T, self.cm[a])
            # drX, drY, drZ distances:
            xyz = np.dot(nsG, gd.cell_cv)
            # mUr            
            mUr = np.dot(xyz,dipole[a])
            #
            Q = qpoles[a,:,:]
            # |r - rcm|
            dis = np.sqrt(((xyz.T)**2).sum(axis=0))
            dis_d = smooth(dis, self.g)
            dis_q = smooth(dis, 0.3)
            # Add dipole component
            potential += mUr.T / dis_d**3
            # Quadrupole components:
            for i in range(3):
                for j in range(3):
                    potential += Q[i,j]*xyz.T[i,:,:,:]*xyz.T[j,:,:,:] / dis_q**5
                    if i == j:
                        potential -= 1./3 * Q[i,j] / dis_q**3

        # Potential updated
        self.initial = False

        # Hold on to
        self.gd = gd
        self.potential = potential
        self.eF  = eF
        self.deF = deF
        self.qpoles = qpoles.copy()
        self.dipoles = dipole.copy()


    def check_convergence(self):
        if self.dipoles_1 is not None:
            dip = abs(self.dipoles - self.dipoles_1).sum()
            qua = abs(self.qpoles - self.qpoles_1).sum()
            if rank == MASTER:
                print dip, qua
            return np.max([dip]) < 1e-5
        else:
            return False


    def get_efield(self, density, setups, nspins):
        """ Evaluate electric field at each cm
            from total psuedo charge density. 

        """
        gd = self.gd

        eF  = np.zeros((3,self.nm))
        deF = np.zeros((3,3,self.nm))

        # Grab comp. charges
        comp = np.zeros(self.qmidx)
        density.Q_aL = {}
        for a, D_sp in density.D_asp.items():
            Q_L = density.Q_aL[a] = np.dot(D_sp[:nspins].sum(0),
                                           setups[a].Delta_pL)
            Q_L[0] += setups[a].Delta0
            comp[a] += Q_L[0]

        # Collect over gd domains
        #wfs.gd.comm.sum(comp)
        comp *= -1*sqrt(4.*pi)

        # Grab pseudo-density on gd
        if density.nt_sg is None:
            density.interpolate_pseudo_density()
        nt_sG = density.nt_sg
        #
        if density.nspins == 1:
            nt_g = nt_sG[0]
        else:
            nt_g = nt_sG.sum(axis=0)

        #assert np.shape(nt_g) == np.shape(self.gd)
        #
        sG = (np.indices(gd.n_c, float).T + \
              gd.beg_c) / gd.N_c
        # Arrays
        #
        for a, pos in enumerate(self.cm):
            # Get all gpt distances relative to molecule a
            nsG = sG - np.linalg.solve(gd.cell_cv.T, self.cm[a])
            # r(xyz) to all gpts
            xyz = np.dot(nsG, gd.cell_cv)
            # distance to all gpts
            dis = np.sqrt(((xyz.T)**2).sum(axis=0))
            # dis = smooth(dis, self.g)
            # total field on cm due to density
            eFT = (xyz.T)*nt_g*gd.dv / dis**3
            eF[:,a] += [eFT[0].sum(),eFT[1].sum(),eFT[2].sum()]
            # nuclei-to-dipole
            xyz_n = self.qm.get_positions() / Bohr - pos
            dis_n = np.sqrt((xyz_n**2).sum(axis=1))
            eF[:,a] -= (xyz_n.T * comp  / dis_n**3).T.sum(axis=0)
            # Loop for deF 
            for n in range(3):
                # ...
                nr_ir = xyz.T[n]*xyz.T*nt_g*3.*gd.dv / dis**5
                deF[n,:,a] -= [nr_ir[0].sum(),nr_ir[1].sum(),nr_ir[2].sum()]
                deF[n,n,a] += (nt_g*gd.dv / dis**3).sum()
                # comp
                deF[n,:,a] += 3*(xyz_n.T[n]*xyz_n.T*comp / dis_n**5).T.sum(axis=0)
                deF[n,n,a] -= (comp / dis_n**3).sum()

        gd.comm.sum(eF)
        gd.comm.sum(deF)
       
        # Change units to D/A, D/AA 
        return eF/Bohr**2, deF/Bohr**3


    def get_nuclear_energy(self, nucleus):
        return -1. * nucleus.setup.Z * self.get_value(spos_c = nucleus.spos_c)


    def get_value(self, position=None, spos_c=None):
        """ Potential value as seen by an electron at a
            certain gridpoint """

        if position is None:
            vr = spos_c * self.gd.h_cv * self.gd.N_c
        else:
            vr = position

        dipole = self.dipole
        quad   = self.quad   

        n = len(self.atoms / self.mp)
        self.get_cm(n,self.mp)

        v = 0

        # Eval. external potential at vr
        for a in range(n):
            dr = vr - self.cm[a]
            dis = np.sqrt((dr**2).sum())

            # mUr
            mUr = np.dot(dis,dipole[a])
            Q = quad[:,:,a]

            v += mUr / dis**3

        return v


    def get_taylor(self, position=None, spos_c=None):
        return [[0]]


    def get_cm(self, n):
        cm   = np.zeros((n,3))
        atoms = self.mm
        mp = self.mp

        for i in range(n):
            cm[i,:] += atoms[i*mp:(i+1)*mp].get_center_of_mass() / Bohr

        return cm