Beispiel #1
0
def bilinear_concentric_potential(r_g, dr_g, f_g, ft_g, l1, l2, alpha, rfilt=None):
    """Calculate corrections for concentric functions and potentials::

                 /     _   _a    _   _a    ~   _   _a  ~ _   _a   _
        v      = | f  (r - R ) V(r - R ) - f  (r - R ) V(r - R ) dr
         m1,m2   /  L1,L2                    L1,L2

    where f(r) and ft(r) are bilinear product of two localized functions which
    are radial splines times real spherical harmonics (l1,m1) or (l2,m2) and::

          _       1       _ -1              ~ _    erf(alpha*r)  _ -1
        V(r) = --------- |r|        ^       V(r) = ------------ |r|
               4 pi eps0                            4 pi eps0

    Note that alpha (and rfilt) should conform with the cutoff radius.
    """
    work_g = erf(alpha*r_g)

    if rfilt is None:
        M = np.vdot(f_g - ft_g * work_g, r_g * dr_g)
    else:
        M = np.vdot((f_g - ft_g * work_g)[r_g>=rfilt], \
            (r_g * dr_g)[r_g>=rfilt])

        # Replace 1/r -> (3-r^2/rfilt^2)/(2*rfilt) for r < rfilt
        M += np.vdot((f_g - ft_g * work_g)[r_g<rfilt], \
            (r_g**2/(2*rfilt) * (3-(r_g/rfilt)**2) * dr_g)[r_g<rfilt])

    v_mm = np.empty((2*l1+1, 2*l2+1), dtype=float)
    for m1 in range(2*l1+1):
        for m2 in range(2*l2+1):
            v_mm[m1,m2] = M * intYY(l1, m1-l1, l2, m2-l2)
    return v_mm
Beispiel #2
0
    def __init__(self, cell_cv, nk_c, txt=sys.stdout):
        self.nk_c = nk_c
        bigcell_cv = cell_cv * nk_c[:, np.newaxis]
        L_c = (np.linalg.inv(bigcell_cv)**2).sum(0)**-0.5
        
        rc = 0.5 * L_c.min()
        prnt('Inner radius for %dx%dx%d Wigner-Seitz cell: %.3f Ang' %
             (tuple(nk_c) + (rc * Bohr,)), file=txt)
        
        self.a = 5 / rc
        prnt('Range-separation parameter: %.3f Ang^-1' % (self.a / Bohr),
             file=txt)
        
#        nr_c = [get_efficient_fft_size(2 * int(L * self.a * 1.5))
        nr_c = [get_efficient_fft_size(2 * int(L * self.a * 3.0))
                for L in L_c]
        prnt('FFT size for calculating truncated Coulomb: %dx%dx%d' %
             tuple(nr_c), file=txt)
        
        self.gd = GridDescriptor(nr_c, bigcell_cv, comm=mpi.serial_comm)
        v_R = self.gd.empty()
        v_i = v_R.ravel()
        
        pos_iv = self.gd.get_grid_point_coordinates().reshape((3, -1)).T
        corner_jv = np.dot(np.indices((2, 2, 2)).reshape((3, 8)).T, bigcell_cv)
        for i, pos_v in enumerate(pos_iv):
            r = ((pos_v - corner_jv)**2).sum(axis=1).min()**0.5
            if r == 0:
                v_i[i] = 2 * self.a / pi**0.5
            else:
                v_i[i] = erf(self.a * r) / r
                
        self.K_Q = np.fft.fftn(v_R) * self.gd.dv
Beispiel #3
0
    def __init__(self, cell_cv, nk_c, txt=sys.stdout):
        self.nk_c = nk_c
        bigcell_cv = cell_cv * nk_c[:, np.newaxis]
        L_c = (np.linalg.inv(bigcell_cv)**2).sum(0)**-0.5

        rc = 0.5 * L_c.min()
        print('Inner radius for %dx%dx%d Wigner-Seitz cell: %.3f Ang' %
              (tuple(nk_c) + (rc * Bohr, )),
              file=txt)

        self.a = 5 / rc
        print('Range-separation parameter: %.3f Ang^-1' % (self.a / Bohr),
              file=txt)

        #        nr_c = [get_efficient_fft_size(2 * int(L * self.a * 1.5))
        nr_c = [get_efficient_fft_size(2 * int(L * self.a * 3.0)) for L in L_c]
        print('FFT size for calculating truncated Coulomb: %dx%dx%d' %
              tuple(nr_c),
              file=txt)

        self.gd = GridDescriptor(nr_c, bigcell_cv, comm=mpi.serial_comm)
        v_R = self.gd.empty()
        v_i = v_R.ravel()

        pos_iv = self.gd.get_grid_point_coordinates().reshape((3, -1)).T
        corner_jv = np.dot(np.indices((2, 2, 2)).reshape((3, 8)).T, bigcell_cv)
        for i, pos_v in enumerate(pos_iv):
            r = ((pos_v - corner_jv)**2).sum(axis=1).min()**0.5
            if r == 0:
                v_i[i] = 2 * self.a / pi**0.5
            else:
                v_i[i] = erf(self.a * r) / r

        self.K_Q = np.fft.fftn(v_R) * self.gd.dv
 def __init__(self, gd, a=19., center=None):
     self.gd = gd
     self.xyz, self.r2 = coordinates(gd, center)
     self.r = np.sqrt(self.r2)
     self.set_width(a)
     self.exp_ar2 = exp(-self.a * self.r2) 
     self.erf_sar = erf(sqrt(self.a) * self.r)
Beispiel #5
0
def dipole_correction(c, gd, rhot_g):
    """Get dipole corrections to charge and potential.

    Returns arrays drhot_g and dphit_g such that if rhot_g has the
    potential phit_g, then rhot_g + drhot_g has the potential
    phit_g + dphit_g, where dphit_g is an error function.

    The error function is chosen so as to be largely constant at the
    cell boundaries and beyond.
    """
    # This implementation is not particularly economical memory-wise

    moment = gd.calculate_dipole_moment(rhot_g)[c]
    if abs(moment) < 1e-12:
        return gd.zeros(), gd.zeros(), 0.0

    r_g = gd.get_grid_point_coordinates()[c]
    cellsize = abs(gd.cell_cv[c, c])
    sr_g = 2.0 / cellsize * r_g - 1.0  # sr ~ 'scaled r'
    alpha = 12.0  # should perhaps be variable
    drho_g = sr_g * np.exp(-alpha * sr_g**2)
    moment2 = gd.calculate_dipole_moment(drho_g)[c]
    factor = -moment / moment2
    drho_g *= factor
    phifactor = factor * (np.pi / alpha)**1.5 * cellsize**2 / 4.0
    dphi_g = -phifactor * erf(sr_g * np.sqrt(alpha))
    return drho_g, dphi_g, phifactor
Beispiel #6
0
 def __init__(self, gd, a=19., center=None):
     self.gd = gd
     self.xyz, self.r2 = coordinates(gd, center)
     self.r = np.sqrt(self.r2)
     self.set_width(a)
     self.exp_ar2 = exp(-self.a * self.r2)
     self.erf_sar = erf(sqrt(self.a) * self.r)
Beispiel #7
0
    def get_dipole_correction(self, gd, rhot_g):
        """Get dipole corrections to charge and potential.

        Returns arrays drhot_g and dphit_g such that if rhot_g has the
        potential phit_g, then rhot_g + drhot_g has the potential
        phit_g + dphit_g, where dphit_g is an error function.
        
        The error function is chosen so as to be largely constant at the
        cell boundaries and beyond.
        """
        # This implementation is not particularly economical memory-wise
        if not gd.orthogonal:
            raise ValueError('Dipole correction requires orthorhombic cell')
        
        c = self.c
        moment = gd.calculate_dipole_moment(rhot_g)[c]
        if abs(moment) < 1e-12:
            return gd.zeros(), gd.zeros()

        r_g = gd.get_grid_point_coordinates()[c]
        cellsize = abs(gd.cell_cv[c, c])
        sr_g = 2.0 / cellsize * r_g - 1.0 # sr ~ 'scaled r'
        alpha = 12.0 # should perhaps be variable
        drho_g = sr_g * np.exp(-alpha * sr_g**2)
        moment2 = gd.calculate_dipole_moment(drho_g)[c]
        factor = -moment / moment2
        drho_g *= factor
        phifactor = factor * (np.pi / alpha)**1.5 * cellsize**2 / 4.0
        dphi_g = -phifactor * erf(sr_g * np.sqrt(alpha))
        return drho_g, dphi_g
Beispiel #8
0
def screen_potential(r, v, charge):
    """Split long-range potential into short-ranged contributions.

    The potential v is a long-ranted potential with the asymptotic form Z/r
    corresponding to the given charge.
    
    Return a potential vscreened and charge distribution rhocomp such that

      v(r) = vscreened(r) + vHartree[rhocomp](r).

    The returned quantities are truncated to a reasonable cutoff radius.
    """
    vr = v * r + charge
    
    err = 0.0
    i = len(vr)
    while err < 1e-5:
        # Things can be a bit sensitive to the threshold.  The O.pz-mt setup
        # gets 20-30 Bohr long compensation charges if it's 1e-6.
        i -= 1
        err = abs(vr[i])
    i += 1
    
    icut = np.searchsorted(r, r[i] * 1.1)
    rcut = r[icut]
    rshort = r[:icut]

    a = rcut / 5.0 # XXX why is this so important?
    vcomp = charge * erf(rshort / (np.sqrt(2.0) * a)) / rshort
    # XXX divide by r
    rhocomp = charge * (np.sqrt(2.0 * np.pi) * a)**(-3) * \
        np.exp(-0.5 * (rshort / a)**2)
    vscreened = v[:icut] + vcomp
    return vscreened, rhocomp
Beispiel #9
0
    def __init__(self, alpha1=10.0, alpha2=300.0):
        self.alpha1 = alpha1
        self.alpha2 = alpha2

        self.natoms = 0
        self.E = 0.0
        self.Z = 1
        self.Nc = 0
        self.Nv = 1
        self.nao = None
        self.pt_j = []
        self.ni = 0
        self.l_j = []
        self.n_j = []
        self.nct = Spline(0, 0.5, [0.0, 0.0, 0.0])
        self.Nct = 0.0
        self.N0_p = []
        rc = 2.0
        r_g = np.linspace(0, rc, 100)
        r2_g = r_g**2
        self.ghat_l = [
            Spline(0, rc,
                   4 * alpha1**1.5 / np.pi**0.5 * np.exp(-alpha1 * r2_g))
        ]
        v_g = erf(alpha1**0.5 * r_g) - erf(alpha2**0.5 * r_g)
        v_g[1:] *= (4 * np.pi)**0.5 / r_g[1:]
        v_g[0] = 4 * (alpha1**0.5 - alpha2**0.5)
        self.vbar = Spline(0, rc, v_g)
        self.Delta_pL = np.zeros((0, 1))
        self.Delta0 = -1 / (4 * np.pi)**0.5
        self.lmax = 0
        self.K_p = self.M_p = self.MB_p = np.zeros(0)
        self.M_pp = np.zeros((0, 0))
        self.Kc = 0.0
        self.MB = 0.0
        self.M = -(alpha1 / 2 / np.pi)**0.5
        self.xc_correction = None
        self.HubU = None
        self.dO_ii = np.zeros((0, 0))
        self.type = 'all-electron'
        self.fingerprint = None
        self.symbol = 'H'
Beispiel #10
0
    def get_electrostatic_potential(self, r, r_B, q_B, excludefroml0=None):
        """...
        
        Calculates the electrostatic potential at point r_i from point
        charges at {r_B} in a lattice using the Ewald summation.
        
        Charge neutrality is obtained by adding the homogenous density
        q_hom/V::
        
                        ---- ----'                             -
                        \    \         q_j            q_hom   /      1
          phi(r_i)  =    |    |   ---------------  +  -----   |dr ---------
                        /    /    |r_i - r_j + l|       V     |   |r - r_i|
                        ---- ----                             / 
                         j    l
        
        r_B : matrix with the lattice basis (in cartesian coordinates).
        
        q_B : probe charges (in units of e).
        
        excludefroml0 : integer specifying if a point charge is not to
        be included in the central (l=0) unit cell. Used for Madelung
        constants.
        """
        E0 = 0.
        if excludefroml0 is None:
            excludefroml0 = np.zeros(len(q_B), dtype=int)
        if excludefroml0 in range(len(q_B)):
            i = excludefroml0
            excludefroml0 = np.zeros(len(q_B), dtype=int)
            excludefroml0[i] = 1

        assert sum(excludefroml0) <= 1

        for i, q in enumerate(q_B):  # potential from point charges
            rprime = r - r_B[i]
            absr = np.linalg.norm(rprime)
            E0 += q * self.get_sum_real_ij(rprime)
            E0 += q * self.get_sum_recip_ij(rprime)
            if excludefroml0[i]:  # if sum over l not 0
                if absr < 1e-14:
                    # lim r -> 0 erf(r G) / r = 2 * G / sqrt(pi)
                    E0 -= q * 2. * self.G / np.sqrt(pi)
                else:
                    E0 -= q * erf(absr * self.G) / absr
            else:  # if sum over all l
                E0 += q * erfc(absr * self.G) / absr

        # correct for compensating homogeneous background
        q_hom = -sum(q_B)
        E0 += q_hom * pi / (self.G**2 * self.Vcell)

        return E0
Beispiel #11
0
    def get_electrostatic_potential(self, r, r_B, q_B, excludefroml0=None):
        """...
        
        Calculates the electrostatic potential at point r_i from point
        charges at {r_B} in a lattice using the Ewald summation.
        
        Charge neutrality is obtained by adding the homogenous density
        q_hom/V::
        
                        ---- ----'                             -
                        \    \         q_j            q_hom   /      1
          phi(r_i)  =    |    |   ---------------  +  -----   |dr ---------
                        /    /    |r_i - r_j + l|       V     |   |r - r_i|
                        ---- ----                             / 
                         j    l
        
        r_B : matrix with the lattice basis (in cartesian coordinates).
        
        q_B : probe charges (in units of e).
        
        excludefroml0 : integer specifying if a point charge is not to
        be included in the central (l=0) unit cell. Used for Madelung
        constants.
        """
        E0 = 0.
        if excludefroml0 is None:
            excludefroml0 = np.zeros(len(q_B), dtype=int)
        if excludefroml0 in range(len(q_B)):
            i = excludefroml0
            excludefroml0 = np.zeros(len(q_B), dtype=int)
            excludefroml0[i] = 1
        
        assert sum(excludefroml0) <= 1

        for i, q in enumerate(q_B): # potential from point charges
            rprime = r - r_B[i]
            absr = np.linalg.norm(rprime)
            E0 += q * self.get_sum_real_ij(rprime)
            E0 += q * self.get_sum_recip_ij(rprime)
            if excludefroml0[i]: # if sum over l not 0
                if absr < 1e-14:
                    # lim r -> 0 erf(r G) / r = 2 * G / sqrt(pi)
                    E0 -= q * 2. * self.G / np.sqrt(pi)
                else:
                    E0 -= q * erf(absr * self.G) / absr
            else: # if sum over all l
                E0 += q * erfc(absr * self.G) / absr

        # correct for compensating homogeneous background
        q_hom = -sum(q_B)
        E0 += q_hom * pi / (self.G**2 * self.Vcell)
        
        return E0
Beispiel #12
0
def bilinear_concentric_force(r_g, dr_g, f_g, ft_g, l1, l2, alpha, rfilt=None):
    """Calculate corrections for concentric functions and potentials::

        _        /     _   _a  __    _   _a    ~   _   _a  __  ~ _   _a   _
        F      = | f  (r - R ) \/_ V(r - R ) - f  (r - R ) \/_ V(r - R ) dr
         m1,m2   /  L1,L2        r               L1,L2       r

    where f(r) and ft(r) are bilinear product of two localized functions which
    are radial splines times real spherical harmonics (l1,m1) or (l2,m2) and::

          _       1       _ -1              ~ _    erf(alpha*r)  _ -1
        V(r) = --------- |r|        ^       V(r) = ------------ |r|
               4 pi eps0                            4 pi eps0

    Note that alpha (and rfilt) should conform with the cutoff radius.
    """
    work_g = erf(alpha*r_g) - 2*alpha/np.pi**0.5 \
        * r_g * np.exp(-alpha**2 * r_g**2)

    if rfilt is None:
        M = - np.vdot(f_g - ft_g * work_g, dr_g)
    else:
        M = - np.vdot((f_g - ft_g * work_g)[r_g>=rfilt], dr_g[r_g>=rfilt])

        # Replace 1/r -> (3-r^2/rfilt^2)/(2*rfilt) for r < rfilt
        work_g = (r_g/rfilt) * erf(alpha*r_g) - alpha*rfilt/np.pi**0.5 \
            * (3-(r_g/rfilt)**2) * np.exp(-alpha**2 * r_g**2)
        M += - np.vdot((f_g * (r_g/rfilt) - ft_g * work_g)[r_g<rfilt], \
            ((r_g/rfilt)**2 * dr_g)[r_g<rfilt])

    F_mmv = np.empty((2*l1+1, 2*l2+1, 3), dtype=float)
    for m1 in range(2*l1+1):
        for m2 in range(2*l2+1):
            F_mmv[m1,m2,0] = M * intYY_ex(l1, m1-l1, l2, m2-l2)
            F_mmv[m1,m2,1] = M * intYY_ey(l1, m1-l1, l2, m2-l2)
            F_mmv[m1,m2,2] = M * intYY_ez(l1, m1-l1, l2, m2-l2)
    return F_mmv
Beispiel #13
0
def I1(R, ap1, b, alpha, beta, m=0):
    if ap1 == (0, 0, 0):
        if b != (0, 0, 0):
            return I1(-R, b, ap1, beta, alpha, m)
        else:
            f = 2 * np.sqrt(np.pi**5 / (alpha + beta)) / (alpha * beta)
            if np.sometrue(R):
                T = alpha * beta / (alpha + beta) * np.dot(R, R)
                f1 = f * erf(T**0.5) * (np.pi / T)**0.5
                if m == 0:
                    return 0.5 * f1
                f2 = f * np.exp(-T) / T**m
                if m == 1:
                    return 0.25 * f1 / T - 0.5 * f2
                if m == 2:
                    return 0.375 * f1 / T**2 - 0.5 * f2 * (1.5 + T)
                if m == 3:
                    return 0.9375 * f1 / T**3 - 0.25 * f2 * (7.5 +
                                                             T * (5 + 2 * T))
                if m == 4:
                    return 3.28125 * f1 / T**4 - 0.125 * f2 * \
                           (52.5 + T * (35 + 2 * T * (7 + 2 * T)))
                if m == 5:
                    return 14.7656 * f1 / T**5 - 0.03125 * f2 * \
                           (945 + T * (630 + T * (252 + T * (72 + T * 16))))
                if m == 6:
                    return 81.2109 * f1 / T**6 - 0.015625 * f2 * \
                           (10395 + T *
                            (6930 + T *
                             (2772 + T * (792 + T * (176 + T * 32)))))
                else:
                    raise NotImplementedError
                
            return f / (1 + 2 * m)
    for i in range(3):
        if ap1[i] > 0:
            break
    a = ap1[:i] + (ap1[i] - 1,) + ap1[i + 1:]
    result = beta / (alpha + beta) * R[i] * I1(R, a, b, alpha, beta, m + 1)
    if a[i] > 0:
        am1 = a[:i] + (a[i] - 1,) + a[i + 1:]
        result += a[i] / (2 * alpha) * (I1(R, am1, b, alpha, beta, m) -
                                        beta / (alpha + beta) *
                                        I1(R, am1, b, alpha, beta, m + 1))
    if b[i] > 0:
        bm1 = b[:i] + (b[i] - 1,) + b[i + 1:]
        result += b[i] / (2 * (alpha + beta)) * I1(R,
                                                   a, bm1, alpha, beta, m + 1)
    return result
Beispiel #14
0
def I1(R, ap1, b, alpha, beta, m=0):
    if ap1 == (0, 0, 0):
        if b != (0, 0, 0):
            return I1(-R, b, ap1, beta, alpha, m)
        else:
            f = 2 * np.sqrt(np.pi**5 / (alpha + beta)) / (alpha * beta)
            if np.sometrue(R):
                T = alpha * beta / (alpha + beta) * np.dot(R, R)
                f1 = f * erf(T**0.5) * (np.pi / T)**0.5
                if m == 0:
                    return 0.5 * f1
                f2 = f * np.exp(-T) / T**m
                if m == 1:
                    return 0.25 * f1 / T - 0.5 * f2
                if m == 2:
                    return 0.375 * f1 / T**2 - 0.5 * f2 * (1.5 + T)
                if m == 3:
                    return 0.9375 * f1 / T**3 - 0.25 * f2 * (7.5 + T *
                                                             (5 + 2 * T))
                if m == 4:
                    return 3.28125 * f1 / T**4 - 0.125 * f2 * \
                        (52.5 + T * (35 + 2 * T * (7 + 2 * T)))
                if m == 5:
                    return 14.7656 * f1 / T**5 - 0.03125 * f2 * \
                        (945 + T * (630 + T * (252 + T * (72 + T * 16))))
                if m == 6:
                    return 81.2109 * f1 / T**6 - 0.015625 * f2 * \
                        (10395 + T *
                         (6930 + T *
                          (2772 + T * (792 + T * (176 + T * 32)))))
                else:
                    raise NotImplementedError

            return f / (1 + 2 * m)
    for i in range(3):
        if ap1[i] > 0:
            break
    a = ap1[:i] + (ap1[i] - 1, ) + ap1[i + 1:]
    result = beta / (alpha + beta) * R[i] * I1(R, a, b, alpha, beta, m + 1)
    if a[i] > 0:
        am1 = a[:i] + (a[i] - 1, ) + a[i + 1:]
        result += a[i] / (
            2 * alpha) * (I1(R, am1, b, alpha, beta, m) - beta /
                          (alpha + beta) * I1(R, am1, b, alpha, beta, m + 1))
    if b[i] > 0:
        bm1 = b[:i] + (b[i] - 1, ) + b[i + 1:]
        result += b[i] / (2 *
                          (alpha + beta)) * I1(R, a, bm1, alpha, beta, m + 1)
    return result
Beispiel #15
0
 def f(self, f):
     if self.width is None:
         if f > self.eps:
             return sqrt(f)
         else:
             return 0.0
     else:
         dEH = -f
         w = self.width
         if dEH / w < -100:
             return sqrt(f)
         Knew = -0.5 * erf(sqrt((max(0.0,dEH)-dEH)/w)) * \
                 sqrt(w*pi) * exp(-dEH/w)
         Knew += 0.5 * sqrt(w * pi) * exp(-dEH / w)
         Knew += sqrt(max(0.0, dEH) - dEH) * exp(max(0.0, dEH) / w)
         #print dEH, w, dEH/w, Knew, f**0.5
         return Knew
 def f(self, f):
     if self.width is None:
         if f > self.eps:
             return sqrt(f)
         else:
             return 0.0
     else:
         dEH = -f
         w = self.width
         if dEH / w < -100:
             return sqrt(f)
         Knew = -0.5 * erf(sqrt((max(0.0,dEH)-dEH)/w)) * \
                 sqrt(w*pi) * exp(-dEH/w)
         Knew += 0.5 * sqrt(w*pi)*exp(-dEH/w)
         Knew += sqrt(max(0.0,dEH)-dEH)*exp(max(0.0,dEH)/w)
         #print dEH, w, dEH/w, Knew, f**0.5
         return Knew
    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.get_reciprocal_vectors()
        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
Beispiel #18
0
    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.get_reciprocal_vectors()
        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
Beispiel #19
0
    def distribution(self, kpt, fermilevel):
        x = (kpt.eps_n - fermilevel) / self.width
        x = x.clip(-100, 100)

        z = 0.5 * (1 - erf(x))
        for i in range(self.iter):
            z += self.coff_function(i + 1) * self.hermite_poly(2 * i + 1, x) * np.exp(-x**2)
        kpt.f_n[:] = kpt.weight * z
        n = kpt.f_n.sum()

        dnde = 1. / np.sqrt(pi) * np.exp(-x**2)
        for i in range(self.iter):
            dnde += self.coff_function(i + 1) * self.hermite_poly(2 * i + 2, x) * np.exp(-x**2)
        dnde = dnde.sum()
        dnde /= self.width
        e_entropy = 0.5 * self.coff_function(self.iter) * self.hermite_poly(2 * self.iter, x)* np.exp(-x**2)
        e_entropy = kpt.weight * e_entropy.sum() * self.width

        sign = 1 - kpt.s * 2
        return np.array([n, dnde, n * sign, e_entropy])
Beispiel #20
0
    def distribution(self, kpt, fermilevel):
        x = (kpt.eps_n - fermilevel) / self.width
        x = x.clip(-100, 100)

        z = 0.5 * (1 - erf(x))
        for i in range(self.iter):
            z += self.coff_function(i + 1) * self.hermite_poly(2 * i + 1, x) * np.exp(-x**2)
        kpt.f_n[:] = kpt.weight * z
        n = kpt.f_n.sum()

        dnde = 1 / np.sqrt(pi) * np.exp(-x**2)
        for i in range(self.iter):
            dnde += self.coff_function(i + 1) * self.hermite_poly(2 * i + 2, x) * np.exp(-x**2)
        dnde = dnde.sum()
        dnde *= kpt.weight / self.width
        e_entropy = 0.5 * self.coff_function(self.iter) * self.hermite_poly(2 * self.iter, x)* np.exp(-x**2)
        e_entropy = kpt.weight * e_entropy.sum() * self.width

        sign = 1 - kpt.s * 2
        return np.array([n, dnde, n * sign, e_entropy])
Beispiel #21
0
def screen_potential(r, v, charge, rcut=None, a=None):
    """Split long-range potential into short-ranged contributions.

    The potential v is a long-ranted potential with the asymptotic form Z/r
    corresponding to the given charge.

    Return a potential vscreened and charge distribution rhocomp such that

      v(r) = vscreened(r) + vHartree[rhocomp](r).

    The returned quantities are truncated to a reasonable cutoff radius.
    """
    vr = v * r + charge

    if rcut is None:
        err = 0.0
        i = len(vr)
        while err < 1e-4:
            # Things can be a bit sensitive to the threshold.  The O.pz-mt
            # setup gets 20-30 Bohr long compensation charges if it's 1e-6.
            i -= 1
            err = abs(vr[i])
        i += 1

        icut = np.searchsorted(r, r[i] * 1.1)
    else:
        icut = np.searchsorted(r, rcut)
    rcut = r[icut]
    rshort = r[:icut].copy()
    if rshort[0] < 1e-16:
        rshort[0] = 1e-10

    if a is None:
        a = rcut / 5.0  # XXX why is this so important?
    vcomp = np.zeros_like(rshort)
    vcomp = charge * erf(rshort / (np.sqrt(2.0) * a)) / rshort
    # XXX divide by r
    rhocomp = charge * (np.sqrt(2.0 * np.pi) * a)**(-3) * \
        np.exp(-0.5 * (rshort / a)**2)
    vscreened = v[:icut] + vcomp
    return vscreened, rhocomp
Beispiel #22
0
    def get_dipole_correction(self, gd, rhot_g):
        """Get dipole corrections to charge and potential.

        Returns arrays drhot_g and dphit_g such that if rhot_g has the
        potential phit_g, then rhot_g + drhot_g has the potential
        phit_g + dphit_g, where dphit_g is an error function.

        The error function is chosen so as to be largely constant at the
        cell boundaries and beyond.
        """
        # This implementation is not particularly economical memory-wise

        c = self.c

        # Right now the dipole correction must be along one coordinate
        # axis and orthogonal to the two others.  The two others need not
        # be orthogonal to each other.
        for c1 in range(3):
            if c1 != c:
                if np.vdot(gd.cell_cv[c], gd.cell_cv[c1]) > 1e-12:
                    raise ValueError('Dipole correction axis must be '
                                     'orthogonal to the two other axes.')

        moment = gd.calculate_dipole_moment(rhot_g)[c]
        if abs(moment) < 1e-12:
            return gd.zeros(), gd.zeros()

        r_g = gd.get_grid_point_coordinates()[c]
        cellsize = abs(gd.cell_cv[c, c])
        sr_g = 2.0 / cellsize * r_g - 1.0  # sr ~ 'scaled r'
        alpha = 12.0  # should perhaps be variable
        drho_g = sr_g * np.exp(-alpha * sr_g**2)
        moment2 = gd.calculate_dipole_moment(drho_g)[c]
        factor = -moment / moment2
        drho_g *= factor
        phifactor = factor * (np.pi / alpha)**1.5 * cellsize**2 / 4.0
        dphi_g = -phifactor * erf(sr_g * np.sqrt(alpha))
        return drho_g, dphi_g
Beispiel #23
0
    def __init__(self, cell_cv, nk_c, txt=None):
        txt = txt or sys.stdout
        self.nk_c = nk_c
        bigcell_cv = cell_cv * nk_c[:, np.newaxis]
        L_c = (np.linalg.inv(bigcell_cv)**2).sum(0)**-0.5

        rc = 0.5 * L_c.min()
        print('Inner radius for %dx%dx%d Wigner-Seitz cell: %.3f Ang' %
              (tuple(nk_c) + (rc * Bohr,)), file=txt)

        self.a = 5 / rc
        print('Range-separation parameter: %.3f Ang^-1' % (self.a / Bohr),
              file=txt)

        nr_c = [get_efficient_fft_size(2 * int(L * self.a * 3.0))
                for L in L_c]
        print('FFT size for calculating truncated Coulomb: %dx%dx%d' %
              tuple(nr_c), file=txt)

        self.gd = GridDescriptor(nr_c, bigcell_cv, comm=mpi.serial_comm)
        v_ijk = self.gd.empty()

        pos_ijkv = self.gd.get_grid_point_coordinates().transpose((1, 2, 3, 0))
        corner_xv = np.dot(np.indices((2, 2, 2)).reshape((3, 8)).T, bigcell_cv)

        # Ignore division by zero (in 0,0,0 corner):
        with seterr(invalid='ignore'):
            # Loop over first dimension to avoid too large ndarrays.
            for pos_jkv, v_jk in zip(pos_ijkv, v_ijk):
                # Distances to the 8 corners:
                d_jkxv = pos_jkv[:, :, np.newaxis] - corner_xv
                r_jk = (d_jkxv**2).sum(axis=3).min(2)**0.5
                v_jk[:] = erf(self.a * r_jk) / r_jk

        # Fix 0/0 corner value:
        v_ijk[0, 0, 0] = 2 * self.a / pi**0.5

        self.K_Q = np.fft.fftn(v_ijk) * self.gd.dv
Beispiel #24
0
    def get_dipole_correction(self, gd, rhot_g):
        """Get dipole corrections to charge and potential.

        Returns arrays drhot_g and dphit_g such that if rhot_g has the
        potential phit_g, then rhot_g + drhot_g has the potential
        phit_g + dphit_g, where dphit_g is an error function.
        
        The error function is chosen so as to be largely constant at the
        cell boundaries and beyond.
        """
        # This implementation is not particularly economical memory-wise
        
        c = self.c
        
        # Right now the dipole correction must be along one coordinate
        # axis and orthogonal to the two others.  The two others need not
        # be orthogonal to each other.
        for c1 in range(3):
            if c1 != c:
                if np.vdot(gd.cell_cv[c], gd.cell_cv[c1]) > 1e-12:
                    raise ValueError('Dipole correction axis must be '
                                     'orthogonal to the two other axes.')
        
        moment = gd.calculate_dipole_moment(rhot_g)[c]
        if abs(moment) < 1e-12:
            return gd.zeros(), gd.zeros()

        r_g = gd.get_grid_point_coordinates()[c]
        cellsize = abs(gd.cell_cv[c, c])
        sr_g = 2.0 / cellsize * r_g - 1.0 # sr ~ 'scaled r'
        alpha = 12.0 # should perhaps be variable
        drho_g = sr_g * np.exp(-alpha * sr_g**2)
        moment2 = gd.calculate_dipole_moment(drho_g)[c]
        factor = -moment / moment2
        drho_g *= factor
        phifactor = factor * (np.pi / alpha)**1.5 * cellsize**2 / 4.0
        dphi_g = -phifactor * erf(sr_g * np.sqrt(alpha))
        return drho_g, dphi_g
Beispiel #25
0
    def load_gauss(self, center=None):
        """Load compensating charge distribution for charged systems.

        See Appendix B of
        A. Held and M. Walter, J. Chem. Phys. 141, 174108 (2014).
        """
        # XXX Check if update is needed (dielectric changed)?
        epsr, dx_epsr, dy_epsr, dz_epsr = self.dielectric.eps_gradeps
        gauss = Gaussian(self.gd, center=center)
        rho_g = gauss.get_gauss(0)
        phi_g = gauss.get_gauss_pot(0)
        x, y, z = gauss.xyz
        fac = 2. * np.sqrt(gauss.a) * np.exp(-gauss.a * gauss.r2)
        fac /= np.sqrt(np.pi) * gauss.r2
        fac -= erf(np.sqrt(gauss.a) * gauss.r) / (gauss.r2 * gauss.r)
        fac *= 2.0 * 1.7724538509055159
        dx_phi_g = fac * x
        dy_phi_g = fac * y
        dz_phi_g = fac * z
        sp = dx_phi_g * dx_epsr + dy_phi_g * dy_epsr + dz_phi_g * dz_epsr
        rho = epsr * rho_g - 1. / (4. * np.pi) * sp
        invnorm = np.sqrt(4. * np.pi) / self.gd.integrate(rho)
        self.phi_gauss = phi_g * invnorm
        self.rho_gauss = rho * invnorm
Beispiel #26
0
    epsinf = 80.
    gauss = Gaussian(gd, center=(box / 2. + epsshift) * np.ones(3) / Bohr)
    eps = gauss.get_gauss(0)
    eps = epsinf - eps / eps.max() * (epsinf - 1.)

    rho = gd.zeros()
    phi_expected = gd.zeros()
    grad_eps = gradient(gd, eps - epsinf, nn)

    for q, shift in zip(qs, shifts):
        gauss = Gaussian(gd, center=(box / 2. + shift) * np.ones(3) / Bohr)
        phi_tmp = gauss.get_gauss_pot(0)
        xyz = gauss.xyz
        fac = 2. * np.sqrt(gauss.a) * np.exp(-gauss.a * gauss.r2)
        fac /= np.sqrt(np.pi) * gauss.r2
        fac -= erf(np.sqrt(gauss.a) * gauss.r) / (gauss.r2 * gauss.r)
        fac *= 2.0 * 1.7724538509055159
        grad_phi = fac * xyz
        laplace_phi = -4. * np.pi * gauss.get_gauss(0)
        rho_tmp = -1. / (4. * np.pi) * (
            (grad_eps * grad_phi).sum(0) + eps * laplace_phi)
        norm = gd.integrate(rho_tmp)
        rho_tmp /= norm * q
        phi_tmp /= norm * q
        rho += rho_tmp
        phi_expected += phi_tmp

    # PolarizationPoissonSolver does not pass this test
    for ps in psolvers[:-1]:
        phi = solve(ps, eps, rho)
        parprint(ps, np.abs(phi - phi_expected).max())
Beispiel #27
0
def erfc(x):
    """The complimentary error function."""
    return 1. - erf(x)
Beispiel #28
0
# 1           0.842701
# 3           0.999978
# I           0. + 1.65043 I
# 1 + I       1.31615 + 0.190453 I
# 0.3 + 3*I   1467.69 - 166.561 I
# 3 - 0.3*I   1.00001 - 0.0000228553 I
values = [
    [ 1, 0.84270079295+0j],
    [ 3, 0.999977909503+0j],
    [ 1j, 1.6504257588j],
    [ 1+1j, 1.3161512816979477+0.19045346923783471j ],
    [ 0.3 + 3j, 1467.69028322-166.560924526j ],
    [ 3 + 0.3j, 0.99997602085736015+2.1863701577230078e-06j]]

maxerror = 1.e-10

for test in values:
    z, res = test
    try:
        r = z.real
    except:
        r = z
    error = abs(res / cerf(z) - 1.)
    if error < maxerror:
        print 'z=', z, ' ok (error=', error, ')'
    else:
        print z, res, cerf(z), erf(r), error
        string = ('error for erf(' + str(z) +') = ' + str(error) + 
                  ' > ' + str(maxerror)) 
        assert(error < maxerror), string
Beispiel #29
0
 def spillage(alpha):
     """Fraction of gaussian charge outside rc."""
     x = alpha * rc**2
     return 1 - erf(sqrt(x)) + 2 * sqrt(x / pi) * exp(-x)
Beispiel #30
0
# Mathematica says:
#   z             erf(z)
# 1           0.842701
# 3           0.999978
# I           0. + 1.65043 I
# 1 + I       1.31615 + 0.190453 I
# 0.3 + 3*I   1467.69 - 166.561 I
# 3 - 0.3*I   1.00001 - 0.0000228553 I
values = [[1, 0.84270079295 + 0j], [3, 0.999977909503 + 0j],
          [1j, 1.6504257588j],
          [1 + 1j, 1.3161512816979477 + 0.19045346923783471j],
          [0.3 + 3j, 1467.69028322 - 166.560924526j],
          [3 + 0.3j, 0.99997602085736015 + 2.1863701577230078e-06j]]

maxerror = 1.e-10

for test in values:
    z, res = test
    try:
        r = z.real
    except:
        r = z
    error = abs(res / cerf(z) - 1.)
    if error < maxerror:
        print('z=', z, ' ok (error=', error, ')')
    else:
        print(z, res, cerf(z), erf(r), error)
        string = ('error for erf(' + str(z) + ') = ' + str(error) + ' > ' +
                  str(maxerror))
        assert (error < maxerror), string
Beispiel #31
0
    def __init__(self, calc, xc=None, kpts=None, bands=None, ecut=150.0,
                 alpha=0.0, skip_gamma=False,
                 world=mpi.world, txt=sys.stdout):
    
        alpha /= Bohr**2

        PairDensity.__init__(self, calc, ecut, world=world, txt=txt)

        ecut /= Hartree
        
        if xc is None:
            self.exx_fraction = 1.0
            xc = XC(XCNull())
        if xc == 'PBE0':
            self.exx_fraction = 0.25
            xc = XC('HYB_GGA_XC_PBEH')
        elif xc == 'B3LYP':
            self.exx_fraction = 0.2
            xc = XC('HYB_GGA_XC_B3LYP')
        self.xc = xc
        self.exc = np.nan  # density dependent part of xc-energy
        
        if kpts is None:
            # Do all k-points in the IBZ:
            kpts = range(self.calc.wfs.kd.nibzkpts)
        
        if bands is None:
            # Do all occupied bands:
            bands = [0, self.nocc2]
        
        prnt('Calculating exact exchange contributions for band index',
             '%d-%d' % (bands[0], bands[1] - 1), file=self.fd)
        prnt('for IBZ k-points with indices:',
             ', '.join(str(i) for i in kpts), file=self.fd)
        
        self.kpts = kpts
        self.bands = bands

        shape = (self.calc.wfs.nspins, len(kpts), bands[1] - bands[0])
        self.exxvv_sin = np.zeros(shape)   # valence-valence exchange energies
        self.exxvc_sin = np.zeros(shape)   # valence-core exchange energies
        self.f_sin = np.empty(shape)       # occupation numbers

        # The total EXX energy will not be calculated if we are only
        # interested in a few eigenvalues for a few k-points
        self.exx = np.nan    # total EXX energy
        self.exxvv = np.nan  # valence-valence
        self.exxvc = np.nan  # valence-core
        self.exxcc = 0.0     # core-core

        self.mysKn1n2 = None  # my (s, K, n1, n2) indices
        self.distribute_k_points_and_bands(self.nocc2)
        
        # All occupied states:
        self.mykpts = [self.get_k_point(s, K, n1, n2)
                       for s, K, n1, n2 in self.mysKn1n2]

        # Compensation charge used for molecular calculations only:
        self.beta = None      # e^(-beta*r^2)
        self.ngauss_G = None  # density
        self.vgauss_G = None  # potential

        self.G0 = None  # effective value for |G+q| when |G+q|=0
        
        self.skip_gamma = skip_gamma
        
        if not self.calc.atoms.pbc.any():
            # Set exponent of exp-function to -19 on the boundary:
            self.beta = 4 * 19 * (self.calc.wfs.gd.icell_cv**2).sum(1).max()
            prnt('Gaussian for electrostatic decoupling: e^(-beta*r^2),',
                 'beta=%.3f 1/Ang^2' % (self.beta / Bohr**2), file=self.fd)
        elif skip_gamma:
            prnt('Skip |G+q|=0 term', file=self.fd)
        else:
            # Volume per q-point:
            dvq = (2 * pi)**3 / self.vol / self.calc.wfs.kd.nbzkpts
            qcut = (dvq / (4 * pi / 3))**(1 / 3)
            if alpha == 0.0:
                self.G0 = (4 * pi * qcut / dvq)**-0.5
            else:
                self.G0 = (2 * pi**1.5 * erf(alpha**0.5 * qcut) / alpha**0.5 /
                           dvq)**-0.5
            prnt('G+q=0 term: Integrate e^(-alpha*q^2)/q^2 for',
                 'q<%.3f 1/Ang and alpha=%.3f Ang^2' %
                 (qcut / Bohr, alpha * Bohr**2), file=self.fd)

        # PAW matrices:
        self.V_asii = []  # valence-valence correction
        self.C_aii = []   # valence-core correction
        self.initialize_paw_exx_corrections()
Beispiel #32
0
def erfc(x):
    """The complimentary error function."""
    return 1. - erf(x)
Beispiel #33
0
 def erfc(values):
     return 1. - erf(values)
Beispiel #34
0
 def spillage(alpha):
     """Fraction of gaussian charge outside rc."""
     x = alpha * rc**2
     return 1 - erf(sqrt(x)) + 2 * sqrt(x / pi) * exp(-x)
Beispiel #35
0
 def erfc(values):
     return 1. - erf(values)
Beispiel #36
0
# Mathematica says:
#   z             erf(z)
# 1           0.842701
# 3           0.999978
# I           0. + 1.65043 I
# 1 + I       1.31615 + 0.190453 I
# 0.3 + 3*I   1467.69 - 166.561 I
# 3 - 0.3*I   1.00001 - 0.0000228553 I
values = [[1, 0.84270079295 + 0j], [3, 0.999977909503 + 0j],
          [1j, 1.6504257588j],
          [1 + 1j, 1.3161512816979477 + 0.19045346923783471j],
          [0.3 + 3j, 1467.69028322 - 166.560924526j],
          [3 + 0.3j, 0.99997602085736015 + 2.1863701577230078e-06j]]

maxerror = 1.e-10

for test in values:
    z, res = test
    try:
        r = z.real
    except:
        r = z
    error = abs(res / cerf(z) - 1.)
    if error < maxerror:
        print 'z=', z, ' ok (error=', error, ')'
    else:
        print z, res, cerf(z), erf(r), error
        string = ('error for erf(' + str(z) + ') = ' + str(error) + ' > ' +
                  str(maxerror))
        assert (error < maxerror), string