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
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()
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))
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
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))
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)
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)
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)
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
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
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]))
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)
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))
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
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))
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]))
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]))
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
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
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
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))
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
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]))
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]
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))
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]
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