예제 #1
0
def get_homo_soln_fast():
    global n, Hsp, kx, radius, s, ds, sigma, phix, phix_full

    b = np.zeros(n, dtype=np.cdouble)
    b = np.sqrt(3.0) * Hsp
    z = np.abs(kx * radius * s)
    i0 = spherical_in(0, z, derivative=False)
    di0 = spherical_in(0, z, derivative=True)
    rat = di0 / i0
    print("NUMBER OF POINTS, SIGMAMAX = ", n, np.max(sigma))
    # Construct matrix using array operations
    M = (ds / 2.0 / np.pi) * np.exp(1j * s[:] * sigma[:, None]) * (
        1.0 + np.abs(s[:]) * rat / np.sqrt(3.0) / phix[:, None])

    # Divide each row by the max value in that row
    maxvals = np.amax(np.absolute(M), axis=1)
    M = M / maxvals[:, None]
    b = b / maxvals

    print('Solving matrix equation...\n')
    amp = linalg.solve(M, b, debug=True)  # fourier coefficients
    check = np.dot(M, amp)

    Jh = np.sum(
        (ds / 2.0 / np.pi) * np.exp(1j * s[:] * sigma[:, None]) * amp[:],
        axis=1)
    Hh = np.sum((ds / 2.0 / np.pi) * np.exp(1j * s[:] * sigma[:, None]) *
                amp[:] * (-np.abs(s[:]) / (3.0 * phix_full[:, None])) * rat[:],
                axis=1)
    return Jh, Hh
예제 #2
0
def surface_solution_numerical():  # inward extension of J=-J_p at r=R.
    global n, Jp, kx, L, delta, radius, s, sigmai, phix_full
    global Js, Jscheck, Hs

    Js = -Jp
    amps = np.zeros(n, dtype=np.cdouble)
    Jprefac = np.sqrt(6.0) / (16.0 * np.pi**3) * kx**2 * L / delta
    amps = -np.pi * Jprefac / (kx * radius) * np.exp(-kx * radius * np.abs(s) -
                                                     1j * s * sigmai)
    Jscheck = np.zeros(n, dtype=np.cdouble)
    Hs = np.zeros(n, dtype=np.cdouble)
    print('Solving surface solution...\n')
    pb = ProgressBar(n * n)
    for i in range(n):
        for j in range(n):
            z = np.abs(kx * radius * s[j])
            i0 = spherical_in(0, z, derivative=False)
            di0 = spherical_in(0, z, derivative=True)
            rat = di0 / i0
            Hs[i] = Hs[i] + (ds / 2.0 / np.pi) * np.exp(
                1j * s[j] * sigma[i]) * amps[j] * (-np.abs(s[j]) * rat / 3.0 /
                                                   phix_full[i])
            Jscheck[i] = Jscheck[i] + (ds / 2.0 / np.pi) * np.exp(
                1j * s[j] * sigma[i]) * amps[j]
            pb.update()
예제 #3
0
 def test_spherical_in_recurrence_real(self):
     # https://dlmf.nist.gov/10.51.E4
     n = np.array([1, 2, 3, 7, 12])
     x = 0.12
     assert_allclose(
         spherical_in(n - 1, x) - spherical_in(n + 1, x),
         (2 * n + 1) / x * spherical_in(n, x))
예제 #4
0
def get_homo_soln_slow():
    global n, Hsp, kx, radius, s, ds, sigma, phix, phix_full

    b = np.zeros(n, dtype=np.cdouble)
    b = np.sqrt(3.0) * Hsp
    M = np.zeros((n, n), dtype=np.cdouble)
    for i in range(n):
        for j in range(n):
            z = np.abs(kx * radius * s[j])
            i0 = spherical_in(0, z, derivative=False)
            di0 = spherical_in(0, z, derivative=True)
            rat = di0 / i0
            M[i, j] = (ds / 2.0 / np.pi) * np.exp(1j * s[j] * sigma[i]) * (
                1.0 + np.abs(s[j]) * rat / np.sqrt(3.0) / phix[i])
    for i in range(n):  # scale each row
        maxval = np.amax(np.absolute(M[i, :]))
        M[i, :] = M[i, :] / maxval
        b[i] = b[i] / maxval
    amp = linalg.solve(M, b, debug=True)  # fourier coefficients
    check = np.dot(M, amp)

    Jh = np.zeros(Jp.size, dtype=np.cdouble)
    Hh = np.zeros(Hp.size, dtype=np.cdouble)
    for i in range(n):
        for j in range(n):
            z = np.abs(kx * radius * s[j])
            i0 = spherical_in(0, z, derivative=False)
            di0 = spherical_in(0, z, derivative=True)
            rat = di0 / i0
            Jh[i] = Jh[i] + (ds / 2.0 / np.pi) * np.exp(
                1j * s[j] * sigma[i]) * amp[j]
            Hh[i] = Hh[i] + (ds / 2.0 / np.pi) * np.exp(
                1j * s[j] * sigma[i]) * amp[j] * (-np.abs(s[j]) /
                                                  (3.0 * phix_full[i])) * rat
    return Jh, Hh
예제 #5
0
 def test_spherical_in_recurrence_complex(self):
     # https://dlmf.nist.gov/10.51.E1
     n = np.array([1, 2, 3, 7, 12])
     x = 1.1 + 1.5j
     assert_allclose(
         spherical_in(n - 1, x) - spherical_in(n + 1, x),
         (2 * n + 1) / x * spherical_in(n, x))
예제 #6
0
def h_bc(x, line, p, temp=1e4, radius=1e11, L=1., tau0=1e7, xi=0.0, **kwargs):

    # Quantities derived from line constants
    vth = np.sqrt(2.0 * c.k_B.cgs.value * temp / c.m_p.cgs.value)
    delta = line.nu0 * vth / c.c.cgs.value
    a = line.gamma / (4.0 * np.pi * delta)
    phix = voigtx(a, x)
    sigma = p.beta * x**3. / a
    sigma0 = line.strength / (np.sqrt(np.pi) * delta)
    numden = tau0 / (sigma0 * radius)
    kx = numden * line.strength / delta

    # Frequency and wavenumber variables
    n = len(x)
    dsigma = 2.0 * (p.beta * np.max(x)**3. / a) / (n - 1)
    ds = 2.0 * np.pi / (n * dsigma)
    smax = ds * (n - 1) / 2.
    s = -smax + ds * np.arange(n)
    print('s: ', s)
    # Set up matrix equation solution vector
    b = np.sqrt(3.0) * hsp_analytic(x, line, p, **kwargs)
    print('b: ', b)

    # Bessel functions, derivative of bessel functions, ratio between them
    z = np.abs(kx * radius * s)
    i0 = spherical_in(0, z, derivative=False)
    di0 = spherical_in(0, z, derivative=True)
    rat = di0 / i0

    # Set matrix elements
    M = np.zeros((n, n), dtype=np.cdouble)
    for i in range(n):
        for j in range(n):
            M[i, j] = (ds / 2.0 / np.pi) * np.exp(1j * s[j] * sigma[i]) * \
                (1.0 + np.abs(s[j]) * rat[j] / np.sqrt(3.0) / phix[i])
    print('M:', M)
    # Scale each row
    for i in range(n):
        maxval = np.amax(np.absolute(M[i, :]))
        M[i, :] = M[i, :] / maxval
        b[i] = b[i] / maxval
    print('Scaled M: ', M)
    # Solve the matrix equation for the Fourier amplitudes
    amp = linalg.solve(M, b)
    print('amp: ', amp)

    # Equations for J and H homogeneous
#    Jh=np.zeros(n,dtype=np.cdouble)
    Hh = np.zeros(n, dtype=np.cdouble)
    for i in range(n):
        for j in range(n):
            #            Jh[i]=Jh[i] + (ds/2.0/np.pi)*np.exp(1j*s[j]*sigma[i])*amp[j]
            Hh[i] = Hh[i] + (ds / 2.0 / np.pi) * np.exp(1j * s[j] * sigma[i]) * \
                amp[j] * (-np.abs(s[j]) / (3.0 * phix[i])) * rat[j]

    print('Hh reals: ', np.real(Hh))
    return np.abs(np.real(Hh))
    def test_sph_in_kn_order0(self):
        x = 1.0
        sph_i0 = np.empty((2,))
        sph_i0[0] = spherical_in(0, x)
        sph_i0[1] = spherical_in(0, x, derivative=True)
        sph_i0_expected = np.array([np.sinh(x) / x, np.cosh(x) / x - np.sinh(x) / x ** 2])
        assert_array_almost_equal(r_[sph_i0], sph_i0_expected)

        sph_k0 = np.empty((2,))
        sph_k0[0] = spherical_kn(0, x)
        sph_k0[1] = spherical_kn(0, x, derivative=True)
        sph_k0_expected = np.array([0.5 * pi * exp(-x) / x, -0.5 * pi * exp(-x) * (1 / x + 1 / x ** 2)])
        assert_array_almost_equal(r_[sph_k0], sph_k0_expected)
    def test_sph_in(self):
        # This test reproduces test_basic.TestSpherical.test_sph_in.
        i1n = np.empty((2, 2))
        x = 0.2

        i1n[0][0] = spherical_in(0, x)
        i1n[0][1] = spherical_in(1, x)
        i1n[1][0] = spherical_in(0, x, derivative=True)
        i1n[1][1] = spherical_in(1, x, derivative=True)

        inp0 = i1n[0][1]
        inp1 = i1n[0][0] - 2.0 / 0.2 * i1n[0][1]
        assert_array_almost_equal(i1n[0], np.array([1.0066800127054699381, 0.066933714568029540839]), 12)
        assert_array_almost_equal(i1n[1], [inp0, inp1], 12)
예제 #9
0
    def test_sph_in_kn_order0(self):
        x = 1.
        sph_i0 = np.empty((2,))
        sph_i0[0] = spherical_in(0, x)
        sph_i0[1] = spherical_in(0, x, derivative=True)
        sph_i0_expected = np.array([np.sinh(x)/x,
                                    np.cosh(x)/x-np.sinh(x)/x**2])
        assert_array_almost_equal(r_[sph_i0], sph_i0_expected)

        sph_k0 = np.empty((2,))
        sph_k0[0] = spherical_kn(0, x)
        sph_k0[1] = spherical_kn(0, x, derivative=True)
        sph_k0_expected = np.array([0.5*pi*exp(-x)/x,
                                    -0.5*pi*exp(-x)*(1/x+1/x**2)])
        assert_array_almost_equal(r_[sph_k0], sph_k0_expected)
예제 #10
0
    def test_sph_in(self):
        # This test reproduces test_basic.TestSpherical.test_sph_in.
        i1n = np.empty((2,2))
        x = 0.2

        i1n[0][0] = spherical_in(0, x)
        i1n[0][1] = spherical_in(1, x)
        i1n[1][0] = spherical_in(0, x, derivative=True)
        i1n[1][1] = spherical_in(1, x, derivative=True)

        inp0 = (i1n[0][1])
        inp1 = (i1n[0][0] - 2.0/0.2 * i1n[0][1])
        assert_array_almost_equal(i1n[0],np.array([1.0066800127054699381,
                                                0.066933714568029540839]),12)
        assert_array_almost_equal(i1n[1],[inp0,inp1],12)
예제 #11
0
def imoment(l, m, lmbd, massArray):
    """
    Computes the small i(l, m) inner Yukawa multipole moment of a point mass
    array by evaluating the spherical harmonic and modified spherical bessel
    function at each point-mass position.

    Inputs
    ------
    l : int
        Multipole moment order
    m : int
        Multipole moment order, m < l
    lmbd : float
        Yukawa length scale
    massArray : ndarray
        Nx4 array of point masses [m, x, y, z]

    Returns
    -------
    ilm : complex
        Complex-valued inner multipole moment
    """
    r = np.sqrt(massArray[:, 1]**2 + massArray[:, 2]**2 + massArray[:, 3]**2)
    rids = np.where(r != 0)[0]
    theta = np.arccos(massArray[rids, 3]/r[rids])
    phi = np.arctan2(massArray[rids, 2], massArray[rids, 1]) % (2*np.pi)
    il = sp.spherical_in(l, r/lmbd)
    if l == 0:
        ilm = massArray[:, 0]*il/np.sqrt(4*np.pi)
    else:
        ilm = massArray[:, 0]*il*np.conj(sp.sph_harm(m, l, phi, theta))
    ilm = np.sum(ilm)
    return ilm
예제 #12
0
def imoments(l, lmbd, massArray):
    """
    Computes all i(l, m) inner Yukawa multipole moments of a point mass array
    up to a given maximum order, l. It does so by evaluating the spherical
    harmonic and modified spherical bessel function at each point-mass
    position.

    Inputs
    ------
    l : int
        Maximum multipole moment order
    lmbd : float
        Yukawa length scale
    massArray : ndarray
        Nx4 array of point masses [m, x, y, z]

    Returns
    -------
    ilms : ndarry, complex
        Complex-valued inner Yukawa multipole moments up to order l.
    """
    ilms = np.zeros([l+1, 2*l+1], dtype='complex')
    r = np.sqrt(massArray[:, 1]**2 + massArray[:, 2]**2 + massArray[:, 3]**2)
    rids = np.where(r != 0)[0]
    theta = np.arccos(massArray[rids, 3]/r[rids])
    phi = np.arctan2(massArray[rids, 2], massArray[rids, 1]) % (2*np.pi)

    # Handle q00 case separately to deal with r==0 cases
    i0 = sp.spherical_in(0, r/lmbd)
    ilm = massArray[:, 0]*i0/np.sqrt(4*np.pi)
    ilms[0, l] = np.sum(ilm)

    for n in range(1, l+1):
        il = sp.spherical_in(l, r/lmbd)
        for m in range(n+1):
            ilm = np.conj(sp.sph_harm(m, n, phi[rids], theta[rids]))
            ilm *= massArray[rids, 0]*il[rids]
            ilms[n, l+m] = np.sum(ilm)

    # Moments always satisfy q(l, -m) = (-1)^m q(l, m)*
    ms = np.arange(-l, l+1)
    fac = (-1)**(np.abs(ms))
    ilms += np.conj(np.fliplr(ilms))*fac
    ilms[:, l] /= 2

    return ilms
예제 #13
0
 def test_spherical_in_inf_complex(self):
     # https://dlmf.nist.gov/10.52.E5
     # Ideally, i1n(n, 1j*inf) = 0 and i1n(n, (1+1j)*inf) = (1+1j)*inf, but
     # this appears impossible to achieve because C99 regards any complex
     # value with at least one infinite  part as a complex infinity, so
     # 1j*inf cannot be distinguished from (1+1j)*inf.  Therefore, nan is
     # the correct return value.
     n = 7
     x = np.array([-inf + 0j, inf + 0j, inf * (1 + 1j)])
     assert_allclose(spherical_in(n, x), np.array([-inf, inf, nan]))
 def test_spherical_in_inf_complex(self):
     # http://dlmf.nist.gov/10.52.E5
     # Ideally, i1n(n, 1j*inf) = 0 and i1n(n, (1+1j)*inf) = (1+1j)*inf, but
     # this appears impossible to achieve because C99 regards any complex
     # value with at least one infinite  part as a complex infinity, so
     # 1j*inf cannot be distinguished from (1+1j)*inf.  Therefore, nan is
     # the correct return value.
     n = 7
     x = np.array([-inf + 0j, inf + 0j, inf * (1 + 1j)])
     assert_allclose(spherical_in(n, x), np.array([-inf, inf, nan]))
예제 #15
0
def environment_kernel(environment_Ai, environment_Bj):
    r_i, theta_i, phi_i = environment_Ai
    r_j, theta_j, phi_j = environment_Bj

    n_i = len(r_i)
    n_j = len(r_j)
    r_i = np.broadcast_to(r_i, (n_j, n_i))
    r_j = r_j.reshape(-1, 1)

    part1 = prefactor1 * np.exp(-alpha *
                                (np.power(r_i, 2) + np.power(r_j, 2)) / 2.)
    if opt.approximate:
        part2 = approximate_spherical_in(2 * alpha * r_i * r_j)
    else:
        part2 = spherical_in(
            np.arange(l_max).reshape(-1, 1, 1), 2 * alpha * r_i * r_j)
    part3 = sph_harm(repeated_m, repeated_l, theta_i, phi_i)
    part4 = np.conj(sph_harm(repeated_m, repeated_l, theta_j, phi_j))

    return I_sum(part1, part2, part3, part4)
예제 #16
0
def SimplePole(eps, lmax, beta):
    """ This transforms a simple pole-like expression for the Matsubara Green's function
      G(iOm) = 1/(iOm-eps)
      to Legendre polynomials basis.
      We derived the following relation
       C_l = -(2*l+1) i^l j_l(i*x)/(2*sh(x))
       where x = beta*eps/2
      The bessel function of imaginary argument is modified bessel-function i_l(x) = (-i)^l j_l(i*x), hence we can also write
       C_l = -(2*l+1) (-1)^l i_l(x)/(2*sh(x))

    This is useful to subtract the high frequency tails.
    """
    x = beta*eps/2.
    if abs(x)>25: # because special.sph_in(x) is an exponential function, x should not be large
        res1 = zeros(lmax+1)
        tpv.SimplePoleInside(res1,x,lmax)
        return res1
    
        #s = sign(x)  
        #sx = s*x
        #res1=zeros(lmax+1)
        #for l in xrange(lmax+1):
        #    z = 1.
        #    ak = 1.
        #    dsm = 1.
        #    for k in range(0,l):
        #        ak *= (l-k)*(l+k+1)/(2.*(k+1.))
        #        z *= -1/sx
        #        dsm += ak * z
        #    res1[l] = -0.5 * (2*l+1)*(-s)**l/x * dsm
        #return res1
    l = array(range(lmax+1))
    #iln = special.sph_in(lmax, x)[0]
    iln = special.spherical_in(range(lmax+1), x)
    res = (-1/(2*sinh(x)))*(2*l+1)*(-1)**l * iln
    return res 
 def test_spherical_in_inf_real(self):
     # http://dlmf.nist.gov/10.52.E3
     n = 5
     x = np.array([-inf, inf])
     assert_allclose(spherical_in(n, x), np.array([-inf, inf]))
 def test_spherical_in_recurrence_real(self):
     # http://dlmf.nist.gov/10.51.E4
     n = np.array([1, 2, 3, 7, 12])
     x = 0.12
     assert_allclose(spherical_in(n - 1, x) - spherical_in(n + 1, x), (2 * n + 1) / x * spherical_in(n, x))
 def test_spherical_in_recurrence_complex(self):
     # http://dlmf.nist.gov/10.51.E1
     n = np.array([1, 2, 3, 7, 12])
     x = 1.1 + 1.5j
     assert_allclose(spherical_in(n - 1, x) - spherical_in(n + 1, x), (2 * n + 1) / x * spherical_in(n, x))
예제 #20
0
def compute_cs(pos, nmax, lmax, rcut, alpha, cutoff):

    # compute the overlap matrix
    w = W(nmax)

    # get the norm of the position vectors
    Ris = np.linalg.norm(pos, axis=1) # (Nneighbors)

    # initialize Gauss Chebyshev Quadrature
    GCQuadrature, weight = GaussChebyshevQuadrature(nmax,lmax) #(Nquad)
    weight *= rcut/2
    # transform the quadrature from (-1,1) to (0, rcut)
    Quadrature = rcut/2*(GCQuadrature+1)

    # compute the arguments for the bessel functions
    BesselArgs = 2*alpha*np.outer(Ris,Quadrature)#(Nneighbors x Nquad)

    # initalize the arrays for the bessel function values
    # and the G function values
    Bessels = np.zeros((len(Ris), len(Quadrature), lmax+1), dtype=np.float64) #(Nneighbors x Nquad x lmax+1)
    Gs = np.zeros((nmax, len(Quadrature)), dtype=np.float64) # (nmax, nquad)

    # compute the g values
    for n in range(1,nmax+1,1):
        Gs[n-1,:] = g(Quadrature, n, nmax, rcut, w)

    # compute the bessel values
    for l in range(lmax+1):
        Bessels[:,:,l] = spherical_in(l, BesselArgs)

    # mutliply the terms in the integral separate from the Bessels
    Quad_Squared = Quadrature**2
    Gs *= Quad_Squared * np.exp(-alpha*Quad_Squared) * np.sqrt(1-GCQuadrature**2) * weight

    # perform the integration with the Bessels
    integral_array = np.einsum('ij,kjl->kil', Gs, Bessels) # (Nneighbors x nmax x lmax+1)

    # compute the gaussian for each atom and multiply with 4*pi
    # to minimize floating point operations
    # weight can also go here since the Chebyshev gauss quadrature weights are uniform
    exparray = 4*np.pi*np.exp(-alpha*Ris**2) # (Nneighbors)

    cutoff_array = cutoff(Ris, rcut)

    exparray *= cutoff_array

    # get the spherical coordinates of each atom
    thetas = np.arccos(pos[:,2]/Ris[:])
    phis = np.arctan2(pos[:,1], pos[:,0])

    # determine the size of the m axis
    msize = 2*lmax+1
    # initialize an array for the spherical harmonics
    ylms = np.zeros((len(Ris), lmax+1, msize), dtype=np.complex128)

    # compute the spherical harmonics
    for l in range(lmax+1):
        for m in range(-l,l+1,1):
            midx = msize//2 + m
            ylms[:,l,midx] = sph_harm(m, l, phis, thetas)

    # multiply the spherical harmonics and the radial inner product
    Y_mul_innerprod = np.einsum('ijk,ilj->iljk', ylms, integral_array)

    # multiply the gaussians into the expression
    C = np.einsum('i,ijkl->ijkl', exparray, Y_mul_innerprod)
    return C
예제 #21
0
def build_SOAP0_kernels(npoints,lcut,natmax,nspecies,nat,nneigh,length,theta,phi,efact,nnmax,vrb,nlist):
    """Compute the L=0 SOAP kernel according to Eqns.(33-34) of Ref. Phys. Rev. B 87, 184115 (2013)"""

    mcut = 2*lcut+1
    divfac = np.array([1.0/float(2*l+1) for l in range(lcut+1)])

    # Precompute spherical harmonics evaluated at the direction of atomic positions
    sph_i6 = np.zeros((npoints,natmax,nspecies,nnmax,lcut+1,mcut), dtype=complex)
    for i in range(npoints):                                    # Loop over configurations
        for ii in range(nat[i]):                                # Loop over all the atomic centers of that configuration
            for ix in range(nspecies):                          # Loop over all the different kind of species
                for iii in range(nneigh[i,ii,ix]):              # Loop over the neighbors of that species around that center of that configuration
                    for l in range(lcut+1):                     # Loop over angular momentum channels
                        for im in range(2*l+1):                 # Loop over the 2*l+1 components of the l subspace
                            m = im-l
                            sph_i6[i,ii,ix,iii,l,im] = special.sph_harm(m,l,phi[i,ii,ix,iii],theta[i,ii,ix,iii])
    sph_j6 = np.conj(sph_i6)

    # Precompute the kernel of local environments considering each atom species to be independent from each other
    skernel = np.zeros((npoints,npoints,natmax,natmax), dtype=float)
    for i in range(npoints):
        for j in range(npoints):
            for ii in range(nat[i]):
                for jj in range(nat[j]):
                    # Compute power spectrum I(x,x') for each atomic species
                    ISOAP = np.zeros((nspecies,lcut+1,mcut,mcut),dtype=complex)
                    for ix in range(nspecies):

                        # Precompute modified spherical Bessel functions of the first kind
                        sph_in = np.zeros((nneigh[i,ii,ix],nneigh[j,jj,ix],lcut+1),dtype=complex)
                        for iii in range(nneigh[i,ii,ix]):
                             for jjj in range(nneigh[j,jj,ix]):
                                 sph_in[iii,jjj,:] = special.spherical_in(lcut,length[i,ii,ix,iii]*length[j,jj,ix,jjj])

                        # Perform contraction over neighbour indexes
                        ISOAP[ix,:,:,:] = np.einsum('a,b,abl,alm,blk->lmk',
                                         efact[i,ii,ix,0:nneigh[i,ii,ix]], efact[j,jj,ix,0:nneigh[j,jj,ix]], sph_in[:,:,:],
                                         sph_i6[i,ii,ix,0:nneigh[i,ii,ix],:,:], sph_j6[j,jj,ix,0:nneigh[j,jj,ix],:,:]   )

                    # Compute the dot product of power spectra contracted over l,m,k and summing over all pairs of atomic species a,b
                    skernel[i,j,ii,jj] = np.real(com_spe.combine_spectra(lcut,mcut,nspecies,ISOAP,divfac))

    # Compute global kernel between structures, averaging over all the (normalized) kernels of local environments
    kernel = np.zeros((npoints,npoints),dtype=float)
    kloc = np.zeros((npoints,npoints,natmax,natmax),dtype=float)
    for i in range(npoints):
        for j in range(npoints):
            for ii in range(nat[i]):
                for jj in range(nat[j]):
                    kloc[i,j,ii,jj] = skernel[i,j,ii,jj] / np.sqrt(skernel[i,i,ii,ii]*skernel[j,j,jj,jj])
                    kernel[i,j] += kloc[i,j,ii,jj]
            kernel[i,j] /= float(nat[i]*nat[j])

    kernels = [kloc,kernel]

    # If needed, compute kernels which arise from exponentiation of the local environment kernels to a power n
    skernelsq = np.zeros((npoints,npoints,natmax,natmax),dtype=float)
    skerneln  = np.zeros((npoints,npoints,natmax,natmax),dtype=float)
    for i,j in product(range(npoints),range(npoints)):
        for ii,jj in product(range(nat[i]),range(nat[j])):
            skernelsq[i,j,ii,jj] = skernel[i,j,ii,jj]*skernel[i,j,ii,jj]
    for n in nlist:
        if n!=0:
            for i,j in product(range(npoints),range(npoints)):
                for ii,jj in product(range(nat[i]),range(nat[j])):
                    skerneln[i,j,ii,jj] = skernel[i,j,ii,jj]
            for m in range(1,n):
                for i,j in product(range(npoints),range(npoints)):
                    for ii,jj in product(range(nat[i]),range(nat[j])):
                        skerneln[i,j,ii,jj] = skerneln[i,j,ii,jj]*skernelsq[i,j,ii,jj]
            # Compute the nth kernel.
            kerneln = np.zeros((npoints,npoints),dtype=float)
            for i,j in product(range(npoints),range(npoints)):
                for ii,jj in product(range(nat[i]),range(nat[j])):
                    kerneln[i,j] += skerneln[i,j,ii,jj] / np.sqrt(skerneln[i,i,ii,ii]*skerneln[j,j,jj,jj])
        else:
            kerneln = np.zeros((npoints,npoints),dtype=float)
            for i,j in product(range(npoints),range(npoints)):
                for ii,jj in product(range(nat[i]),range(nat[j])):
                    kerneln[i,j] = kernel[i,j]
        kernels.append(kerneln)

    return [kernels]
 def f(self, n, z):
     return spherical_in(n, z)
 def test_spherical_in_d_zero(self):
     n = np.array([1, 2, 3, 7, 15])
     assert_allclose(spherical_in(n, 0, derivative=True), np.zeros(5))
예제 #24
0
 def test_spherical_in_at_zero(self):
     # https://dlmf.nist.gov/10.52.E1
     # But note that n = 0 is a special case: i0 = sinh(x)/x -> 1
     n = np.array([0, 1, 2, 5, 10, 100])
     x = 0
     assert_allclose(spherical_in(n, x), np.array([1, 0, 0, 0, 0, 0]))
예제 #25
0
 def f(self, n, z):
     return spherical_in(n, z)
예제 #26
0
 def test_spherical_in_inf_real(self):
     # https://dlmf.nist.gov/10.52.E3
     n = 5
     x = np.array([-inf, inf])
     assert_allclose(spherical_in(n, x), np.array([-inf, inf]))
예제 #27
0
def chi_l_model(l):
    l = int(l.real)
    kern = lambda e: beta * sqrt(2*l+1) * e *((-np.sign(e)) ** l) * \
                     spherical_in(l, np.abs(e)*beta/2) / np.sinh(np.abs(e)*beta/2) / (2*np.pi)
    return np.diag(chi_norms) * quad(lambda e: dos(e, 1) * kern(e), -1, 3, points = [0])[0].real
예제 #28
0
def chi_auto_l_model(l):
    l = int(l.real)
    kern = lambda e: beta * (1 + (-1) ** l) * sqrt(2*l+1) * e * spherical_in(l, e*beta/2) / np.sinh(e*beta/2) / (2*np.pi)
    return np.diag(chi_auto_norms) * quad(lambda e: 2*dos(e, 0) * kern(e), 0, 2)[0].real
예제 #29
0
def g_zt_l_model(l):
    l = int(l.real)
    kern = lambda e: beta * ((-1) ** (l+1)) * sqrt(2*l+1) * spherical_in(l, e*beta/2)*np.exp(-e*beta/2)
    return np.diag(g_zt_norms) * quad(lambda e: 2*dos(e, 0) * kern(e), 0, 2)[0].real
예제 #30
0
 def test_spherical_in_exact(self):
     # https://dlmf.nist.gov/10.49.E9
     x = np.array([0.12, 1.23, 12.34, 123.45])
     assert_allclose(spherical_in(2, x), (1 / x + 3 / x * x * x) * sinh(x) -
                     3 / x * x * cosh(x))
예제 #31
0
def mgaussian_lcoef(l, ell):
    a_l = 2.0 * (l + 1) * spherical_in(l, 1.0 / ell**2) * np.exp(-1.0 / ell**2)
    a_l[0:lmin + 1] = 0.
    norm = np.sum(a_l)
    return a_l / norm
예제 #32
0
 def df(self, n, z):
     return spherical_in(n, z, derivative=True)
 def test_spherical_in_at_zero(self):
     # http://dlmf.nist.gov/10.52.E1
     # But note that n = 0 is a special case: i0 = sinh(x)/x -> 1
     n = np.array([0, 1, 2, 5, 10, 100])
     x = 0
     assert_allclose(spherical_in(n, x), np.array([1, 0, 0, 0, 0, 0]))
예제 #34
0
 def test_spherical_in_d_zero(self):
     n = np.array([1, 2, 3, 7, 15])
     assert_allclose(spherical_in(n, 0, derivative=True), np.zeros(5))
 def df(self, n, z):
     return spherical_in(n, z, derivative=True)
예제 #36
0
def build_SOAP_kernels(lval, npoints, lcut, natmax, nspecies, nat, nneigh,
                       length, theta, phi, efact, nnmax, vrb, nlist):
    """Compute spherical tensor SOAP kernel of order lval>0 according to Eqns.(S15-S16) of the Suppl. Info. of Ref. arXiv:1709.06757 (2017)"""

    mcut = 2 * lcut + 1
    divfac = np.array(
        [np.sqrt(1.0 / float(2 * l + 1)) for l in xrange(lcut + 1)])

    # Precompute the Clebsch-Gordan coefficients
    CG1 = np.zeros((lcut + 1, lcut + 1, mcut, 2 * lval + 1), dtype=float)
    for l, l1 in product(xrange(lcut + 1), xrange(lcut + 1)):
        for im, iim in product(xrange(2 * l + 1), xrange(2 * lval + 1)):
            CG1[l, l1, im, iim] = CG(lval, iim - lval, l1, im - l - iim + lval,
                                     l, im - l).doit()

    CG2 = np.zeros(
        (lcut + 1, lcut + 1, mcut, mcut, 2 * lval + 1, 2 * lval + 1),
        dtype=float)
    for l, l1 in product(xrange(lcut + 1), xrange(lcut + 1)):
        for im, ik, iim, iik in product(xrange(2 * l + 1), xrange(2 * l + 1),
                                        xrange(2 * lval + 1),
                                        xrange(2 * lval + 1)):
            CG2[l, l1, im, ik, iim,
                iik] = CG1[l, l1, im, iim] * CG1[l, l1, ik,
                                                 iik] * divfac[l] * divfac[l]

    # Precompute spherical harmonics evaluated at the direction of atomic positions
    sph_i6 = np.zeros(
        (npoints, natmax, nspecies, nnmax, lcut + 1, 2 * lcut + 1),
        dtype=complex)
    for i in xrange(npoints):
        for ii in xrange(nat[i]):
            for ix in xrange(nspecies):
                for iii in xrange(nneigh[i, ii, ix]):
                    for l in xrange(lcut + 1):
                        for im in xrange(2 * l + 1):
                            m = im - l
                            sph_i6[i, ii, ix, iii, l, im] = special.sph_harm(
                                m, l, phi[i, ii, ix, iii], theta[i, ii, ix,
                                                                 iii])
    sph_j6 = conj(sph_i6)

    # Precompute the tensorial kernel of local environments considering each atom species to be independent from each other
    skernel = np.zeros(
        (npoints, npoints, natmax, natmax, 2 * lval + 1, 2 * lval + 1),
        complex)
    einpath = None
    listl = np.asarray(xrange(lcut + 1))
    ISOAP = np.zeros((nspecies, lcut + 1, mcut, mcut), dtype=complex)
    for i in xrange(npoints):
        for j in xrange(i + 1):
            for ii, jj in product(xrange(nat[i]), xrange(nat[j])):
                ISOAP[:] = 0.0
                for ix in xrange(nspecies):

                    # Precompute modified spherical Bessel functions of the first kind
                    sph_in = np.zeros(
                        (nneigh[i, ii, ix], nneigh[j, jj, ix], lcut + 1),
                        dtype=float)
                    for iii, jjj in product(xrange(nneigh[i, ii, ix]),
                                            xrange(nneigh[j, jj, ix])):
                        sph_in[iii, jjj, :] = special.spherical_in(
                            listl,
                            length[i, ii, ix, iii] * length[j, jj, ix, jjj])

                    if einpath is None:  # only computes einpath once - assuming number of neighbors is roughly constant
                        einpath = np.einsum_path('a,b,abl,alm,blk->lmk',
                                                 efact[i, ii, ix,
                                                       0:nneigh[i, ii, ix]],
                                                 efact[j, jj, ix,
                                                       0:nneigh[j, jj, ix]],
                                                 sph_in[:, :, :],
                                                 sph_i6[i, ii, ix,
                                                        0:nneigh[i, ii,
                                                                 ix], :, :],
                                                 sph_j6[j, jj, ix,
                                                        0:nneigh[j, jj,
                                                                 ix], :, :],
                                                 optimize='optimal')[0]

                    # Perform contraction over neighbour indexes using the optimized path for Einstein summations
                    ISOAP[ix, :, :, :] = np.einsum('a,b,abl,alm,blk->lmk',
                                                   efact[i, ii, ix,
                                                         0:nneigh[i, ii, ix]],
                                                   efact[j, jj, ix,
                                                         0:nneigh[j, jj, ix]],
                                                   sph_in[:, :, :],
                                                   sph_i6[i, ii, ix,
                                                          0:nneigh[i, ii,
                                                                   ix], :, :],
                                                   sph_j6[j, jj, ix,
                                                          0:nneigh[j, jj,
                                                                   ix], :, :],
                                                   optimize=einpath)

                # Make use of a Fortran 90 subroutine to combine the power spectra and the CG coefficients
                skernel[i, j, ii, jj, :, :] = pow_spec.fill_spectra(
                    lval, lcut, mcut, nspecies, ISOAP, CG2)

                # Exploit Hermiticity
                if not j == i:
                    skernel[j, i, jj, ii, :, :] = np.conj(skernel[i, j, ii,
                                                                  jj, :, :].T)

    # Precompute normalization factors
    norm = np.zeros((npoints, natmax), dtype=float)
    for i in xrange(npoints):
        for ii in xrange(nat[i]):
            norm[i, ii] = 1.0 / np.sqrt(
                np.linalg.norm(skernel[i, i, ii, ii, :, :]))

    # compute the kernel
    kernel = np.zeros((npoints, npoints, 2 * lval + 1, 2 * lval + 1),
                      dtype=complex)
    kloc = np.zeros(
        (npoints, npoints, natmax, natmax, 2 * lval + 1, 2 * lval + 1),
        dtype=complex)
    for i, j in product(xrange(npoints), xrange(npoints)):
        for ii, jj in product(xrange(nat[i]), xrange(nat[j])):
            kloc[i, j, ii,
                 jj, :, :] = skernel[i, j, ii,
                                     jj, :, :] * norm[i, ii] * norm[j, jj]
            kernel[i, j, :, :] += kloc[i, j, ii, jj, :, :]
        kernel[i, j] /= float(nat[i] * nat[j])

    kernels = [kloc, kernel]

    # If needed, compute kernels which arise from exponentiation of the local environment kernels to a power n
    skernelsq = np.zeros(
        (npoints, npoints, natmax, natmax, 2 * lval + 1, 2 * lval + 1),
        complex)
    skerneln = np.zeros(
        (npoints, npoints, natmax, natmax, 2 * lval + 1, 2 * lval + 1),
        complex)
    for i, j in product(xrange(npoints), xrange(npoints)):
        for ii, jj in product(xrange(nat[i]), xrange(nat[j])):
            skernelsq[i, j, ii, jj, :, :] = np.dot(
                np.conj(skernel[i, j, ii, jj, :, :].T), skernel[i, j, ii,
                                                                jj, :, :])
    for n in nlist:
        if n != 0:
            for i, j in product(xrange(npoints), xrange(npoints)):
                for ii, jj in product(xrange(nat[i]), xrange(nat[j])):
                    skerneln[i, j, ii, jj, :, :] = skernel[i, j, ii, jj, :, :]
            for m in xrange(n):
                for i, j in product(xrange(npoints), xrange(npoints)):
                    for ii, jj in product(xrange(nat[i]), xrange(nat[j])):
                        skerneln[i, j, ii, jj, :, :] = np.dot(
                            skerneln[i, j, ii, jj, :, :], skernelsq[i, j, ii,
                                                                    jj, :, :])
            for i in xrange(npoints):
                for ii in xrange(nat[i]):
                    norm[i, ii] = 1.0 / np.sqrt(
                        np.linalg.norm(skerneln[i, i, ii, ii, :, :]))
            # Compute the nth kernel.
            kerneln = np.zeros((npoints, npoints, 2 * lval + 1, 2 * lval + 1),
                               dtype=complex)
            for i, j in product(xrange(npoints), xrange(npoints)):
                for ii, jj in product(xrange(nat[i]), xrange(nat[j])):
                    kerneln[i, j, :, :] += skerneln[i, j, ii, jj, :, :] * norm[
                        i, ii] * norm[j, jj]
                kerneln[i, j] /= float(nat[i] * nat[j])
        else:
            kerneln = np.zeros((npoints, npoints, 2 * lval + 1, 2 * lval + 1),
                               dtype=complex)
            for i, j in product(xrange(npoints), xrange(npoints)):
                kerneln[i, j, :, :] = kernel[i, j, :, :]
        kernels.append(kerneln)

    return [kernels]
예제 #37
0
def compute_dcs(pos, nmax, lmax, rcut, alpha, cutoff):
    # compute the overlap matrix
    w = W(nmax)

    # get the norm of the position vectors
    Ris = np.linalg.norm(pos, axis=1) # (Nneighbors)

    # get unit vectors
    upos = pos/Ris[:,np.newaxis]

    # initialize Gauss Chebyshev Quadrature
    GCQuadrature, weight = GaussChebyshevQuadrature(nmax,lmax) #(Nquad)
    weight *= rcut/2
    # transform from (-1,1) to (0, rcut)
    Quadrature = rcut/2*(GCQuadrature+1)

    # compute the arguments for the bessel functions
    BesselArgs = 2*alpha*np.outer(Ris,Quadrature)#(Nneighbors x Nquad)

    # initalize the arrays for the bessel function values
    # and the G function values
    Bessels = np.zeros((len(Ris), len(Quadrature), lmax+1), dtype=np.float64) #(Nneighbors x Nquad x lmax+1)
    Gs = np.zeros((nmax, len(Quadrature)), dtype=np.float64) # (nmax, nquad)
    dBessels = np.zeros((len(Ris), len(Quadrature), lmax+1), dtype=np.float64) #(Nneighbors x Nquad x lmax+1)

    # compute the g values
    for n in range(1,nmax+1,1):
        Gs[n-1,:] = g(Quadrature, n, nmax, rcut,w)*weight

    # compute the bessel values
    for l in range(lmax+1):
        Bessels[:,:,l] = spherical_in(l, BesselArgs)
        dBessels[:,:,l] = spherical_in(l, BesselArgs, derivative=True)

    #(Nneighbors x Nquad x lmax+1) unit vector here
    gradBessels = np.einsum('ijk,in->ijkn',dBessels,upos)
    gradBessels *= 2*alpha
    # multiply with r for the integral
    gradBessels = np.einsum('ijkn,j->ijkn',gradBessels,Quadrature)

    # mutliply the terms in the integral separate from the Bessels
    Quad_Squared = Quadrature**2
    Gs *= Quad_Squared * np.exp(-alpha*Quad_Squared) * np.sqrt(1-GCQuadrature**2)

    # perform the integration with the Bessels
    integral_array = np.einsum('ij,kjl->kil', Gs, Bessels) # (Nneighbors x nmax x lmax+1)

    grad_integral_array = np.einsum('ij,kjlm->kilm', Gs, gradBessels)# (Nneighbors x nmax x lmax+1, 3)

    # compute the gaussian for each atom
    exparray = 4*np.pi*np.exp(-alpha*Ris**2) # (Nneighbors)

    gradexparray = (-2*alpha*Ris*exparray)[:,np.newaxis]*upos

    cutoff_array = cutoff(Ris, rcut)

    grad_cutoff_array = np.einsum('i,in->in',cutoff(Ris, rcut, True), upos)

    # get the spherical coordinates of each atom
    thetas = np.arccos(pos[:,2]/Ris[:])
    phis = np.arctan2(pos[:,1], pos[:,0])

    # the size changes temporarily for the derivative
    # determine the size of the m axis
    Msize = 2*(lmax+1)+1
    msize = 2*lmax + 1
    # initialize an array for the spherical harmonics and gradients
    #(Nneighbors, l, m, *3*)
    ylms = np.zeros((len(Ris), lmax+1+1, Msize), dtype=np.complex128)
    gradylms = np.zeros((len(Ris), lmax+1, msize, 3), dtype=np.complex128)
    # compute the spherical harmonics
    for l in range(lmax+1+1):
        for m in range(-l,l+1,1):
            midx = Msize//2 + m
            ylms[:,l,midx] = sph_harm(m, l, phis, thetas)


    for l in range(1, lmax+1):
        for m in range(-l, l+1, 1):
            midx = msize//2 + m
            Midx = Msize//2 + m
            # get gradient with recpect to spherical covariant components
            xcov0 = -np.sqrt(((l+1)**2-m**2)/(2*l+1)/(2*l+3))*l*ylms[:,l+1,Midx]/Ris

            if abs(m) <= l-1:
                xcov0 += np.sqrt((l**2-m**2)/(2*l-1)/(2*l+1))*(l+1)*ylms[:,l-1,Midx]/Ris


            xcovpl1 = -np.sqrt((l+m+1)*(l+m+2)/2/(2*l+1)/(2*l+3))*l*ylms[:,l+1,Midx+1]/Ris

            if abs(m+1) <= l-1:
                xcovpl1 -= np.sqrt((l-m-1)*(l-m)/2/(2*l-1)/(2*l+1))*(l+1)*ylms[:,l-1,Midx+1]/Ris


            xcovm1 = -np.sqrt((l-m+1)*(l-m+2)/2/(2*l+1)/(2*l+3))*l*ylms[:,l+1,Midx-1]/Ris

            if abs(m-1) <= l-1:
                xcovm1 -= np.sqrt((l+m-1)*(l+m)/2/(2*l-1)/(2*l+1))*(l+1)*ylms[:,l-1,Midx-1]/Ris

            #transform the gradient to cartesian
            gradylms[:,l,midx,0] = 1/np.sqrt(2)*(xcovm1-xcovpl1)
            gradylms[:,l,midx,1] = 1j/np.sqrt(2)*(xcovm1+xcovpl1)
            gradylms[:,l,midx,2] = xcov0

    # index ylms to get rid of extra terms for derivative
    ylms = ylms[:,0:lmax+1,1:1+2*lmax+1]
    # multiply the spherical harmonics and the radial inner product
    Y_mul_innerprod = np.einsum('ijk,ilj->iljk', ylms, integral_array)
    # multiply the gradient of the spherical harmonics with the radial inner get_radial_inner_product
    dY_mul_innerprod = np.einsum('ijkn,ilj->iljkn', gradylms, integral_array)
    # multiply the spherical harmonics with the gradient of the radial inner get_radial_inner_product
    Y_mul_dinnerprod = np.einsum('ijk,iljn->iljkn', ylms, grad_integral_array)
    # multiply the gaussians into the expression with 4pi
    C = np.einsum('i,ijkl->ijkl', exparray, Y_mul_innerprod)
    # multiply the gradient of the gaussian with the other terms
    gradexp_mul_y_inner = np.einsum('in,ijkl->ijkln', gradexparray, Y_mul_innerprod)
    # add gradient of inner product and spherical harmonic terms
    gradHarmonics_mul_gaussian = np.einsum('ijkln,i->ijkln', dY_mul_innerprod+Y_mul_dinnerprod, exparray)
    dC = gradexp_mul_y_inner + gradHarmonics_mul_gaussian
    dC *= cutoff_array[:,np.newaxis,np.newaxis,np.newaxis,np.newaxis]
    dC += np.einsum('in,ijkl->ijkln', grad_cutoff_array, C)
    C *= cutoff_array[:,np.newaxis,np.newaxis,np.newaxis]
    return C, dC
 def test_spherical_in_exact(self):
     # http://dlmf.nist.gov/10.49.E9
     x = np.array([0.12, 1.23, 12.34, 123.45])
     assert_allclose(spherical_in(2, x), (1 / x + 3 / x ** 3) * sinh(x) - 3 / x ** 2 * cosh(x))
예제 #39
0
                    help='approximate Spherical Bessel function')
opt = parser.parse_args()

structures = read(opt.xyz, format='extxyz', index=':')

l_max = opt.lmax + 1
alpha = opt.alpha
repeated_l = np.repeat(np.arange(l_max), np.arange(1, l_max * 2,
                                                   2)).reshape(-1, 1)
repeated_m = [[m for m in np.arange(-l, l + 1)] for l in np.arange(l_max)]
repeated_m = np.array([item for sublist in repeated_m
                       for item in sublist]).reshape(-1, 1)

# Approximate Modified Spherical Bessel Function of the First Kind
spherical_in_bins = np.linspace(0, alpha * opt.rc * opt.rc, 100000)
spherical_in_cache = spherical_in(
    np.arange(0, l_max).reshape(-1, 1), spherical_in_bins)[np.arange(l_max)]


def approximate_spherical_in(r):
    return np.take(spherical_in_cache,
                   np.digitize(r, spherical_in_bins) - 1,
                   axis=1)


@jit(nopython=True)
def azimuthal_polar(r_vector):
    azimuth_vector = []
    polar_vector = []
    n = len(r_vector)
    for i in range(n):
        x, y, z = r_vector[i]
예제 #40
0
def transl_yuk_z_RR_recurs(LMax, dr, k):
    """
    Translate coaxially from regular to regular, Gumerov and Duraiswami.

    Inputs
    ------
    l : int
        Order of multipole expansion to output rotation matrix coefficient H

    Reference
    ---------
    "Recursions for the computation of multipole translation and rotation
    coefficients for the 3-d helmholtz equation."

    https://arxiv.org/pdf/1403.7698v1.pdf
    """
    # Create (E|F)_{l,m}^{m}
    eflm = np.zeros([2 * LMax + 1, LMax + 1])
    # Start (E|F)_{l, 0}^{0}
    ls = np.arange(2 * LMax + 1)
    eflm[:, 0] = (-1)**ls * np.sqrt(2 * ls + 1) * sp.spherical_in(ls, k * dr)
    # now recursion 4.86 for (E|F)_{l, m}^{m}
    for m in range(LMax):
        for l in range(2 * LMax - m):
            # (n=m)
            blmm = get_bnm(l, -m - 1)
            blmp = get_bnm(l + 1, m)
            bmm = get_bnm(m + 1, -m - 1)
            eflm[l,
                 m + 1] = (eflm[l - 1, m] * blmm - eflm[l + 1, m] * blmp) / bmm

    efms = []
    # Now create (E|F)^m matrices (p-|m|)x(p-|m|)
    # Start with (2*p-|m|)x(p-|m|) and truncate
    for m in range(LMax + 1):
        efm = np.zeros([2 * LMax + 1 - 2 * m, LMax - m + 1])
        efm[:, 0] = eflm[m:2 * LMax + 1 - m, m]
        for n in range(LMax - m):
            for l in range(n + 1, 2 * (LMax - m) - n):
                anm = get_anm(n + m, m)
                alm = get_anm(l + m, m)
                almm = get_anm(l - 1 + m, m)
                anmm = get_anm(n - 1 + m, m)
                print(l, n + 1, l + m, anm, anmm, alm, almm)
                if anmm != 0:
                    print(l, n, m)
                    efm[l,
                        n + 1] = (almm * efm[l - 1, n] - alm * efm[l + 1, n] +
                                  anmm * efm[l, n - 1]) / anm
                else:
                    efm[l, n +
                        1] = (alm * efm[l + 1, n] - almm * efm[l - 1, n]) / anm
        efms.append(efm)

    for m in range(LMax + 1):
        efms[m] = efms[m][:LMax - m + 1, :LMax - m + 1]
        lm = len(efms[m])
        # Start from m since (E|F)^m matrix starts at (m,m) corner
        nls = (np.arange(m, m + lm))
        enl = np.transpose(efms[m]) * np.outer((-1)**(nls), (-1)**(nls))
        efms[m] += enl
        efms[m] -= np.diag(np.diag(enl))

    return eflm, efms