def test_cubicspline_and_deriv(self): """Test spline for derivation.""" odg = OneDGrid(np.arange(10) + 1, np.ones(10), (0, np.inf)) rad = IdentityRTransform().transform_1d_grid(odg) for _ in range(10): degree = np.random.randint(5, 20) atgrid = AtomGrid.from_pruned(rad, 1, sectors_r=[], sectors_degree=[degree]) values = self.helper_func_power(atgrid.points) # spls = atgrid.fit_values(values) for i in range(10): interp = atgrid.interpolate( atgrid.points[atgrid.indices[i]:atgrid.indices[i + 1]], values, deriv=1, ) # same result from points and interpolation ref_deriv = self.helper_func_power_deriv( atgrid.points[atgrid.indices[i]:atgrid.indices[i + 1]]) assert_allclose(interp, ref_deriv) # test random x, y, z with fd for _ in range(10): xyz = np.random.rand(10, 3) * np.random.uniform(1, 6) ref_value = self.helper_func_power_deriv(xyz) interp = atgrid.interpolate(xyz, values, deriv=1) assert_allclose(interp, ref_value)
def setUp(self): """Set up radial grid for integral tests.""" pts = HortonLinear(100) tf = ExpRTransform(1e-3, 1e1) rad_pts = tf.transform(pts.points) rad_wts = tf.deriv(pts.points) * pts.weights self.rgrid = OneDGrid(rad_pts, rad_wts)
def GaussChebyshev(npoints): r"""Generate a grid based on Gauss-Chebyshev grid. Gauss Chebyshev grid is defined as: .. math:: \int_{-1}^{1} \frac{f(x)}{\sqrt{1-x^2}}dx \approx \sum_{i=1}^n w_i f(x_i) This integration grid is defined as : .. math:: \int_{-1}^{1}f(x)dx\approx\sum_{i=1}^n w_i \sqrt{1-x_i^2} f(x_i) = \sum_{i=1}^n w_i' f(x_i) Parameters ---------- npoints : int Number of points in the grid Returns ------- OneDGrid A grid instance with points and weights, [-1, 1] """ # points are generated in decreasing order # weights are pi/n, all weights are the same points, weights = np.polynomial.chebyshev.chebgauss(npoints) weights = weights * np.sqrt(1 - np.power(points, 2)) return OneDGrid(points[::-1], weights)
def test_atomic_rotate(self): """Test random rotation for atomic grid.""" rad_pts = np.array([0.1, 0.5, 1]) rad_wts = np.array([0.3, 0.4, 0.3]) rad_grid = OneDGrid(rad_pts, rad_wts) degs = [3, 5, 7] atgrid = AtomGrid(rad_grid, degrees=degs) # make sure True and 1 is not the same result atgrid1 = AtomGrid(rad_grid, degrees=degs, rotate=True) atgrid2 = AtomGrid(rad_grid, degrees=degs, rotate=1) # test diff points, same weights assert not np.allclose(atgrid.points, atgrid1.points) assert not np.allclose(atgrid.points, atgrid2.points) assert not np.allclose(atgrid1.points, atgrid2.points) assert_allclose(atgrid.weights, atgrid1.weights) assert_allclose(atgrid.weights, atgrid2.weights) assert_allclose(atgrid1.weights, atgrid2.weights) # test same integral value = np.prod(atgrid.points**2, axis=-1) value1 = np.prod(atgrid.points**2, axis=-1) value2 = np.prod(atgrid.points**2, axis=-1) res = atgrid.integrate(value) res1 = atgrid1.integrate(value1) res2 = atgrid2.integrate(value2) assert_almost_equal(res, res1) assert_almost_equal(res1, res2) # test rotated shells for i in range(len(degs)): non_rot_shell = atgrid.get_shell_grid(i).points rot_shell = atgrid2.get_shell_grid(i).points rot_mt = R.random(random_state=1 + i).as_matrix() assert_allclose(rot_shell, non_rot_shell @ rot_mt)
def GaussChebyshev(npoints): r"""Generate 1D grid on [-1, 1] interval based on Gauss-Chebyshev quadrature. The fundamental definition of Gauss-Chebyshev quadrature is: .. math:: \int_{-1}^{1} \frac{f(x)}{\sqrt{1-x^2}} dx \approx \sum_{i=1}^n w_i f(x_i) However, to integrate function :math:`g(x)` over [-1, 1], this is re-written as: .. math:: \int_{-1}^{1}g(x) dx \approx \sum_{i=1}^n w_i \sqrt{1-x_i^2} g(x_i) = \sum_{i=1}^n w_i' g(x_i) Parameters ---------- npoints : int Number of grid points. Returns ------- OneDGrid A 1D grid instance. """ # points are generated in decreasing order # weights are pi/n, all weights are the same points, weights = np.polynomial.chebyshev.chebgauss(npoints) weights = weights * np.sqrt(1 - np.power(points, 2)) return OneDGrid(points[::-1], weights, (-1, 1))
def TrefethenGeneral(npoints, quadrature, d=3): r"""Generate 1D grid on [-1,1] interval based on Trefethen-General. Parameters ---------- npoints : int Number of points in the grid. Returns ------- OneDGrid A 1D grid instance. """ grid = quadrature(npoints) points = np.zeros(npoints) weights = np.zeros(npoints) if d == 2: points = _g2(grid.points) weights = _derg2(grid.points) * grid.weights elif d == 3: points = _g3(grid.points) weights = _derg3(grid.points) * grid.weights else: points = grid.points weights = grid.weights return OneDGrid(points, weights, (-1, 1))
def transform_1d_grid(self, oned_grid): r"""Generate a new integral grid by transforming given the OneDGrid. .. math:: \int^{\inf}_0 g(r) d r &= \int^b_a g(r(x)) \frac{dr}{dx} dx \\ &= \int^b_a f(x) \frac{dr}{dx} dx \\ w^r_n = w^x_n \cdot \frac{dr}{dx} \vert_{x_n} Parameters ---------- oned_grid : OneDGrid An instance of 1D grid. Returns ------- OneDGrid Transformed 1D grid spanning a different domain. """ if not isinstance(oned_grid, OneDGrid): raise TypeError( f"Input grid is not OneDGrid, got {type(oned_grid)}") # check domain if oned_grid.domain[0] < self.domain[0] or oned_grid.domain[ 1] > self.domain[1]: raise ValueError( "Given 1D grid domain does not match the transformation domain.\n" f"grid domain: {oned_grid.domain}, tf domain: {self.domain}") new_points = self.transform(oned_grid.points) new_weights = self.deriv(oned_grid.points) * oned_grid.weights new_domain = oned_grid.domain if new_domain is not None: new_domain = self.transform(np.array(oned_grid.domain)) return OneDGrid(new_points, new_weights, new_domain)
def test_integrating_angular_components(self): """Test radial points that contain zero.""" odg = OneDGrid(np.array([0.0, 1e-16, 1e-8, 1e-4, 1e-2]), np.ones(5), (0, np.inf)) atom_grid = AtomGrid(odg, degrees=[3]) spherical = atom_grid.convert_cartesian_to_spherical() # Evaluate all spherical harmonics on the atomic grid points (r_i, theta_j, phi_j). spherical_harmonics = generate_real_spherical_harmonics( 3, spherical[:, 1], spherical[:, 2] # theta, phi points ) # Convert to three-dimensional array (Degrees, Order, Points) spherical_array = np.zeros((3, 2 * 3 + 1, len(atom_grid.points))) spherical_array[0, 0, :] = spherical_harmonics[0, :] # (l,m) = (0,0) spherical_array[1, 0, :] = spherical_harmonics[1, :] # = (1, 0) spherical_array[1, 1, :] = spherical_harmonics[2, :] # = (1, 1) spherical_array[1, 2, :] = spherical_harmonics[3, :] # = (1, -1) spherical_array[2, 0, :] = spherical_harmonics[4, :] # = (2, 0) spherical_array[2, 1, :] = spherical_harmonics[5, :] # = (2, 2) spherical_array[2, 2, :] = spherical_harmonics[6, :] # = (2, 1) spherical_array[2, 3, :] = spherical_harmonics[7, :] # = (2, -2) spherical_array[2, 4, :] = spherical_harmonics[8, :] # = (2, -1) integral = atom_grid.integrate_angular_coordinates(spherical_array) assert integral.shape == (3, 2 * 3 + 1, 5) # Assert that all spherical harmonics except when l=0,m=0 are all zero. assert_almost_equal(integral[0, 0, :], np.sqrt(4.0 * np.pi)) assert_almost_equal(integral[0, 1:, :], 0.0) assert_almost_equal(integral[1:, :, :], 0.0)
def test_interpolation_of_laplacian_of_exponential(self): r"""Test the interpolation of Laplacian of exponential.""" odg = OneDGrid(np.linspace(0.01, 1, num=1000), np.ones(1000), (0, np.inf)) degree = 10 atgrid = AtomGrid.from_pruned(odg, 1, sectors_r=[], sectors_degree=[degree]) def func(cart_pts): radius = np.linalg.norm(cart_pts, axis=1) return np.exp(-radius) func_values = func(atgrid.points) laplacian = atgrid.interpolate_laplacian(func_values) # Test on the same points used for interpolation and random points. for grid in [atgrid.points, np.random.uniform(-0.5, 0.5, (250, 3))]: actual = laplacian(grid) spherical_pts = atgrid.convert_cartesian_to_spherical(grid) # Laplacian of exponential is e^-x (x - 2) / x desired = (np.exp(-spherical_pts[:, 0]) * (spherical_pts[:, 0] - 2.0) / spherical_pts[:, 0]) assert_almost_equal(actual, desired, decimal=3)
def test_spherical_average_of_spherical_harmonic(self): r"""Test spherical average of spherical harmonic is zero.""" # construct helper function def func(sph_points): # Spherical harmonic of order 6 and magnetic 0 r, phi, theta = sph_points.T return (np.sqrt(13) / (np.sqrt(np.pi) * 32) * (231 * np.cos(theta)**6.0 - 315 * np.cos(theta)**4.0 + 105 * np.cos(theta)**2.0 - 5.0)) # Construct Radial Grid and atomic grid with spherical harmonics of degree 10 # for all points. oned_grid = np.arange(0.0, 5.0, 0.001) rad = OneDGrid(oned_grid, np.ones(len(oned_grid)), (0, np.inf)) atgrid = AtomGrid(rad, degrees=[10]) spherical_pts = atgrid.convert_cartesian_to_spherical(atgrid.points) func_values = func(spherical_pts) spherical_avg = atgrid.spherical_average(func_values) # Test that the spherical average of a spherical harmonic is zero. numb_pts = 1000 random_rad_pts = np.random.uniform(0.02, np.pi, size=(numb_pts, 3)) spherical_avg2 = spherical_avg(random_rad_pts[:, 0]) assert_allclose(spherical_avg2, 0.0, atol=1e-4)
def test_interpolation_of_laplacian_with_spherical_harmonic(self): r"""Test the interpolation of Laplacian of spherical harmonic is eigenvector.""" odg = OneDGrid(np.linspace(0.0, 10, num=2000), np.ones(2000), (0, np.inf)) rad = IdentityRTransform().transform_1d_grid(odg) degree = 6 * 2 + 2 atgrid = AtomGrid.from_pruned(rad, 1, sectors_r=[], sectors_degree=[degree]) def func(sph_points): # Spherical harmonic of order 6 and magnetic 0 r, phi, theta = sph_points.T return (np.sqrt(2.0) * np.sqrt(13) / (np.sqrt(np.pi) * 32) * (231 * np.cos(theta)**6.0 - 315 * np.cos(theta)**4.0 + 105 * np.cos(theta)**2.0 - 5.0)) # Get spherical points from atomic grid spherical_pts = atgrid.convert_cartesian_to_spherical(atgrid.points) func_values = func(spherical_pts) laplacian = atgrid.interpolate_laplacian(func_values) # Test on the same points used for interpolation and random points. for grid in [atgrid.points, np.random.uniform(-0.75, 0.75, (250, 3))]: actual = laplacian(grid) spherical_pts = atgrid.convert_cartesian_to_spherical(grid) # Eigenvector spherical harmonic times l(l + 1) / r^2 with np.errstate(divide="ignore", invalid="ignore"): desired = (-func(spherical_pts) * 6 * (6 + 1) / spherical_pts[:, 0]**2.0) desired[spherical_pts[:, 0]**2.0 < 1e-10] = 0.0 assert_almost_equal(actual, desired, decimal=3)
def test_fitting_product_of_spherical_harmonic(self): r"""Test fitting the radial components of r**2 times spherical harmonic.""" max_degree = 7 # Maximum degree rad = OneDGrid(np.linspace(0.0, 1.0, num=10), np.ones(10), (0, np.inf)) atom_grid = AtomGrid(rad, degrees=[max_degree]) max_degree = atom_grid.l_max spherical = atom_grid.convert_cartesian_to_spherical() spherical_harmonic = generate_real_spherical_harmonics( 4, spherical[:, 1], spherical[:, 2] # theta, phi points ) # Test on the function r^2 * Y^1_3 func_vals = spherical[:, 0]**2.0 * spherical_harmonic[(3 + 1)**2 + 1, :] # Fit radial components fit = atom_grid.radial_component_splines(func_vals) radial_pts = np.arange(0.0, 1.0, 0.01) i = 0 for l_value in range(0, max_degree // 2): for m in ([0] + [x for x in range(1, l_value + 1)] + [x for x in range(-l_value, 0)]): if i != (3 + 1)**2 + 1: assert_almost_equal(fit[i](radial_pts), 0.0, decimal=8) else: # Test that on the right spherical harmonic the function r* Y^1_3 projects # to \rho^{1,3}(r) = r assert_almost_equal(fit[i](radial_pts), radial_pts**2.0) i += 1
def test_spherical_average_of_gaussian(self): r"""Test spherical average of a Gaussian (radial) function is itself and its integral.""" # construct helper function def func(sph_points): return np.exp(-sph_points[:, 0]**2.0) # Construct Radial Grid and atomic grid with spherical harmonics of degree 10 # for all points. oned_grid = np.arange(0.0, 5.0, 0.001) rad = OneDGrid(oned_grid, np.ones(len(oned_grid)), (0, np.inf)) atgrid = AtomGrid(rad, degrees=[5]) spherical_pts = atgrid.convert_cartesian_to_spherical(atgrid.points) func_values = func(spherical_pts) spherical_avg = atgrid.spherical_average(func_values) # Test that the spherical average of a Gaussian is itself numb_pts = 1000 random_rad_pts = np.random.uniform(0.0, 1.5, size=(numb_pts, 3)) spherical_avg2 = spherical_avg(random_rad_pts[:, 0]) func_vals = func(random_rad_pts) assert_allclose(spherical_avg2, func_vals, atol=1e-4) # Test the integral of spherical average is the integral of Gaussian e^(-x^2)e^(-y^2)... # from -infinity to infinity which is equal to pi^(3/2) integral = ( 4.0 * np.pi * np.trapz(y=spherical_avg(oned_grid) * oned_grid**2.0, x=oned_grid)) actual_integral = np.sqrt(np.pi)**3.0 assert_allclose(actual_integral, integral)
def test_cubicspline_and_interp(self): """Test cubicspline interpolation values.""" odg = OneDGrid(np.arange(10) + 1, np.ones(10), (0, np.inf)) rad_grid = IdentityRTransform().transform_1d_grid(odg) for _ in range(10): degree = np.random.randint(5, 20) atgrid = AtomGrid.from_pruned(rad_grid, 1, sectors_r=[], sectors_degree=[degree]) values = self.helper_func_power(atgrid.points) # spls = atgrid.fit_values(values) for i in range(10): interp = atgrid.interpolate( atgrid.points[atgrid.indices[i]:atgrid.indices[i + 1]], values) # same result from points and interpolation assert_allclose( interp, values[atgrid.indices[i]:atgrid.indices[i + 1]]) # test random x, y, z for _ in range(10): xyz = np.random.rand(10, 3) * np.random.uniform(1, 6) # xyz /= np.linalg.norm(xyz, axis=-1)[:, None] # rad = np.random.normal() * np.random.randint(1, 11) # xyz *= rad ref_value = self.helper_func_power(xyz) interp = atgrid.interpolate(xyz, values) assert_allclose(interp, ref_value)
def GaussLaguerre(npoints, alpha=0): r"""Generate 1D grid on [0, inf) interval based on Generalized Gauss-Laguerre quadrature. The fundamental definition of Generalized Gauss-Laguerre quadrature is: .. math:: \int_{0}^{\infty} x^\alpha e^{-x} f(x) dx \approx \sum_{i=1}^n w_i f(x_i) However, to integrate function :math:`g(x)` over [0, inf), this is re-written as: .. math:: \int_{0}^{\infty} g(x)dx \approx \sum_{i=1}^n \frac{w_i}{x_i^\alpha e^{-x_i}} g(x_i) = \sum_{i=1}^n w_i' g(x_i) Parameters ---------- npoints : int Number of grid points. alpha : float, optional Value of parameter :math:`alpha` which should be larger than -1. Returns ------- OneDGrid A 1D grid instance. """ if alpha <= -1: raise ValueError(f"Alpha need to be bigger than -1, given {alpha}") points, weights = roots_genlaguerre(npoints, alpha) weights = weights * np.exp(points) * np.power(points, -alpha) return OneDGrid(points, weights, (0, np.inf))
def test_spline_with_sph_harms(self): """Test spline projection the same as spherical harmonics.""" odg = OneDGrid(np.arange(10) + 1, np.ones(10), (0, np.inf)) rad = IdentityRTransform().transform_1d_grid(odg) atgrid = AtomGrid.from_pruned(rad, 1, sectors_r=[], sectors_degree=[7]) sph_coor = atgrid.convert_cart_to_sph() values = self.helper_func_power(atgrid.points) l_max = atgrid.l_max // 2 r_sph = generate_real_sph_harms(l_max, sph_coor[:, 1], sph_coor[:, 2]) result = spline_with_sph_harms(r_sph, values, atgrid.weights, atgrid.indices, rad) # generate ref for shell in range(1, 11): sh_grid = atgrid.get_shell_grid(shell - 1, r_sq=False) # Convert to spherical harmonics r = np.linalg.norm(sh_grid._points, axis=1) theta = np.arctan2(sh_grid._points[:, 1], sh_grid._points[:, 0]) phi = np.arccos(sh_grid._points[:, 2] / r) # General all spherical harmonics up to l_max r_sph = generate_real_sph_harms(l_max, theta, phi) # Project the function 2x^2 + 3y^2 + 4z^2 r_sph_proj = np.sum( r_sph * self.helper_func_power(sh_grid.points) * sh_grid.weights, axis=-1, ) # Check the actual projection against the cubic spline interpolation. assert_allclose(r_sph_proj, result(shell), atol=1e-10)
def test_convert_points_to_sph(self): """Test convert random points to spherical based on atomic structure.""" rad_pts = np.array([0.1, 0.5, 1]) rad_wts = np.array([0.3, 0.4, 0.3]) rad_grid = OneDGrid(rad_pts, rad_wts) center = np.random.rand(3) atgrid = AtomGrid(rad_grid, degrees=[7], center=center) points = np.random.rand(100, 3) calc_sph = atgrid.convert_cart_to_sph(points) # compute ref result ref_coor = points - center # radius r = np.linalg.norm(ref_coor, axis=-1) # azimuthal theta = np.arctan2(ref_coor[:, 1], ref_coor[:, 0]) # polar phi = np.arccos(ref_coor[:, 2] / r) assert_allclose(np.stack([r, theta, phi]).T, calc_sph) assert_equal(calc_sph.shape, (100, 3)) # test single point point = np.random.rand(3) calc_sph = atgrid.convert_cart_to_sph(point) ref_coor = point - center r = np.linalg.norm(ref_coor) theta = np.arctan2(ref_coor[1], ref_coor[0]) phi = np.arccos(ref_coor[2] / r) assert_allclose(np.array([r, theta, phi]).reshape(-1, 3), calc_sph)
def GaussLaguerre(npoints, alpha=0): r"""Generate a grid based on generalized Gauss-Laguerre grid. Generalizeed Gauss Laguerre grid is defined as: .. math:: \int_{0}^{\infty} x^{\alpha}e^{-x} f(x)dx \approx \sum_{i=1}^n w_i f(x_i) This integration grid is defined as : .. math:: \int_{0}^{\infty} f(x)dx \approx \sum_{i=1}^n \frac{w_i}{x_i^{\alpha} e^{-x_i}} f(x_i) = \sum_{i=1}^n w_i' f(x_i) Parameters ---------- npoints : int Number of points in the grid alpha : float, default to 0, required to be > -1 parameter alpha value Returns ------- OneDGrid A grid instance with points and weights, (0, inf) """ if alpha <= -1: raise ValueError(f"Alpha need to be bigger than -1, given {alpha}") points, weights = roots_genlaguerre(npoints, alpha) weights = weights * np.exp(points) * np.power(points, -alpha) return OneDGrid(points, weights)
def test_total_atomic_grid(self): """Normal initialization test.""" radial_pts = np.arange(0.1, 1.1, 0.1) radial_wts = np.ones(10) * 0.1 rgrid = OneDGrid(radial_pts, radial_wts) rad = 0.5 r_sectors = np.array([0.5, 1, 1.5]) degs = np.array([6, 14, 14, 6]) # generate a proper instance without failing. ag_ob = AtomGrid.from_pruned(rgrid, radius=rad, sectors_r=r_sectors, sectors_degree=degs) assert isinstance(ag_ob, AtomGrid) assert len(ag_ob.indices) == 11 assert ag_ob.l_max == 15 ag_ob = AtomGrid.from_pruned(rgrid, radius=rad, sectors_r=np.array([]), sectors_degree=np.array([6])) assert isinstance(ag_ob, AtomGrid) assert len(ag_ob.indices) == 11 ag_ob = AtomGrid(rgrid, sizes=[110]) assert ag_ob.l_max == 17 assert_array_equal(ag_ob._degs, np.ones(10) * 17) assert ag_ob.size == 110 * 10 # new init AtomGrid ag_ob2 = AtomGrid(rgrid, degrees=[17]) assert ag_ob2.l_max == 17 assert_array_equal(ag_ob2._degs, np.ones(10) * 17) assert ag_ob2.size == 110 * 10 assert isinstance(ag_ob.rgrid, OneDGrid) assert_allclose(ag_ob.rgrid.points, rgrid.points) assert_allclose(ag_ob.rgrid.weights, rgrid.weights)
def GaussChebyshevLobatto(npoints): r"""Generate 1D grid on [-1, 1] interval based on Gauss-Chebyshev-Lobatto. The definition of Gauss-Chebyshev-Lobato quadrature is: .. math:: \int_{-1}^{1} \frac{f(x)}{\sqrt{1-x^2}} dx \approx \sum_{i=1}^n w_i f(x_i) However, to integrate function :math:`g(x)` over [-1, 1], this is re-written as: .. math:: \int_{-1}^{1}g(x) dx \approx \sum_{i=1}^n w_i \sqrt{1-x_i^2} f(x_i) = \sum_{i=1}^n w_i' f(x_i) Where .. math:: x_i = \cos\left( \frac{(i-1)\pi}{n-1} \right) And the weights .. math:: w_{1} = w_{n} = \frac{\pi}{2(n-1)} And the internal weights .. math:: w_{i\neq 1,n} = \frac{\pi}{n-1} Parameters ---------- npoints : int Number of points in the grid Returns ------- OneDGrid A 1D grid instance. """ if npoints <= 1: raise ValueError("npoints must be greater that one, given {npoints}") idx = np.arange(npoints) weights = np.ones(npoints) idx = (idx * np.pi) / (npoints - 1) points = np.cos(idx) points = points[::-1] weights *= np.pi / (npoints - 1) weights *= np.sqrt(1 - np.power(points, 2)) weights[0] /= 2 weights[npoints - 1] = weights[0] return OneDGrid(points, weights, (-1, 1))
def RectangleRuleSine(npoints): r"""Generate 1D grid on (0:1) interval based on rectangle rule. The fundamental definition of this quadrature is: .. math:: \int_{0}^{1} f(x) dx \approx \sum_{i=1}^n w_i f(x_i) The range of integration can be modified by :math: `q = 2 x - 1`. .. math:: 2 \int_{0}^{1} f(x) dx = \int_{-1}^{1} f(q) dq Where .. math:: x_i = \frac{2 i - 1}{2 n} And the weights .. math:: w_i = \frac{2}{n^2 \pi} \sin(n\pi x_i) \sin^2(n\pi /2) + \frac{4}{n \pi}\sum_{m=1}^{n-1} \frac{\sin(m \pi x_i)\sin^2(m\pi /2)} {m} Parameters ---------- npoints : int Number of points in the grid. Returns ------- OneDGrid A 1D grid instance. """ if npoints <= 1: raise ValueError("npoints must be greater that one, given {npoints}") idx = np.arange(npoints) + 1 points = (2 * idx - 1) / (2 * npoints) weights = ( (2 / (npoints * np.pi ** 2)) * np.sin(npoints * np.pi * points) * np.sin(npoints * np.pi / 2) ** 2 ) m = np.arange(npoints - 1) + 1 bm = np.sin(m * np.pi / 2) ** 2 / m sim = np.sin(np.outer(m * np.pi, points)) wi = bm @ sim weights += (4 / (npoints * np.pi)) * wi points = 2 * points - 1 weights *= 2 return OneDGrid(points, weights, (-1, 1))
def test_find_l_for_rad_list(self): """Test private method find_l_for_rad_list.""" radial_pts = np.arange(0.1, 1.1, 0.1) radial_wts = np.ones(10) * 0.1 rgrid = OneDGrid(radial_pts, radial_wts) rad = 1 r_sectors = np.array([0.2, 0.4, 0.8]) degs = np.array([3, 5, 7, 3]) atomic_grid_degree = AtomGrid._find_l_for_rad_list( rgrid.points, rad * r_sectors, degs) assert_equal(atomic_grid_degree, [3, 3, 5, 5, 7, 7, 7, 7, 3, 3])
def test_cubicspline_and_interp_mol(self): """Test cubicspline interpolation values.""" odg = OneDGrid(np.arange(10) + 1, np.ones(10), (0, np.inf)) rad = IdentityRTransform().transform_1d_grid(odg) atgrid = AtomGrid.from_pruned(rad, 1, sectors_r=[], sectors_degree=[7]) values = self.helper_func_power(atgrid.points) result = spline_with_atomic_grid(atgrid, values) sph_coor = atgrid.convert_cart_to_sph() semi_sph_c = sph_coor[atgrid.indices[5] : atgrid.indices[6]] interp = interpolate(result, rad.points[5], semi_sph_c[:, 1], semi_sph_c[:, 2]) # same result from points and interpolation assert_allclose(interp, values[atgrid.indices[5] : atgrid.indices[6]])
def RectangleRuleSineEndPoints(npoints): r"""Generate 1D grid on [-1:1] interval based on rectangle rule. The fundamental definition of this quadrature is: .. math:: \int_{-1}^{1} f(x) dx \approx \sum_{i=1}^n w_i f(x_i) The range of integration can be modified by :math: `q = 2 x - 1`. .. math:: 2 \int_{0}^{1} f(x) dx = \int_{-1}^{1} f(q) dq Where .. math:: x_i = \frac{i}{n+1} And the weights .. math:: w_i = \frac{2}{n+1} \sum_{m=1}^n \frac{\sin(m \pi x_i)(1-\cos(m \pi))}{m \pi} Parameters ---------- npoints : int Number of points in the grid. Returns ------- OneDGrid A 1D grid instance. """ if npoints <= 1: raise ValueError("npoints must be greater that one, given {npoints}") idx = np.arange(npoints) + 1 points = idx / (npoints + 1) weights = np.zeros(npoints) m = np.arange(npoints) + 1 bm = (np.ones(npoints) - np.cos(m * np.pi)) / (m * np.pi) sim = np.sin(np.outer(m * np.pi, points)) weights = bm @ sim points = 2 * points - 1 weights *= 4 / (npoints + 1) return OneDGrid(points, weights, (-1, 1))
def TanhSinh(npoints, delta=0.1): r"""Generate 1D grid on [-1,1] interval based on Tanh-Sinh rule. The fundamental definition is: .. math:: \int_{-1}^{1} f(x) dx \approx \sum_{i=-\frac{1}{2}(n-1)}^{\frac{1}{2}(n-1)} w_i f(x_i) Where .. math:: x_i = \tanh\left( \frac{\pi}{2} \sinh(i\delta) \right) And the weights .. math:: w_i = \frac{\frac{\pi}{2}\delta \cosh(i\delta)} {\cosh^2(\frac{\pi}{2}\sinh(i\delta))} Parameters ---------- npoints : int Number of points in the grid, this value must be odd. delta : float This values is a parameter :math:`\delta`, is related with the size. Returns ------- OneDGrid A 1D grid instance. """ if npoints <= 1: raise ValueError("npoints must be greater that one, given {npoints}") if npoints % 2 == 0: raise ValueError("npoints must be odd, given {npoints}") points = np.zeros(npoints) weights = np.zeros(npoints) j = int((1 - npoints) / 2) + np.arange(npoints) theta = j * delta points = np.tanh(np.pi * np.sinh(theta) / 2) weights = ( np.pi * delta * np.cosh(theta) / (2 * np.cosh(np.pi * np.sinh(theta) / 2) ** 2) ) return OneDGrid(points, weights, (-1, 1))
def test_cubicspline_and_interp_mol(self): """Test cubicspline interpolation values.""" odg = OneDGrid(np.arange(10) + 1, np.ones(10), (0, np.inf)) rad = IdentityRTransform().transform_1d_grid(odg) atgrid = AtomGrid.from_pruned(rad, 1, sectors_r=[], sectors_degree=[7]) values = self.helper_func_power(atgrid.points) # spls = atgrid.fit_values(values) for i in range(10): interp = atgrid.interpolate( atgrid.points[atgrid.indices[i]:atgrid.indices[i + 1]], values) # same result from points and interpolation assert_allclose(interp, values[atgrid.indices[i]:atgrid.indices[i + 1]])
def test_atomic_grid(self): """Test atomic grid center transilation.""" rad_pts = np.array([0.1, 0.5, 1]) rad_wts = np.array([0.3, 0.4, 0.3]) rad_grid = OneDGrid(rad_pts, rad_wts) degs = np.array([3, 5, 7]) # origin center # randome center pts, wts, ind = AtomGrid._generate_atomic_grid(rad_grid, degs) ref_pts, ref_wts, ref_ind = AtomGrid._generate_atomic_grid(rad_grid, degs) # diff grid points diff by center and same weights assert_allclose(pts, ref_pts) assert_allclose(wts, ref_wts)
def test_derivative_of_interpolate_given_splines(self): """Test the spline interpolating derivative of function in radial coordinate.""" odg = OneDGrid(np.arange(10) + 1, np.ones(10), (0, np.inf)) rad = IdentityRTransform().transform_1d_grid(odg) atgrid = AtomGrid.from_pruned(rad, 1, sectors_r=[], sectors_degree=[7]) sph_coor = atgrid.convert_cart_to_sph() values = self.helper_func_power(atgrid.points) l_max = atgrid.l_max // 2 r_sph = generate_real_sph_harms(l_max, sph_coor[:, 1], sph_coor[:, 2]) result = spline_with_sph_harms(r_sph, values, atgrid.weights, atgrid.indices, rad) semi_sph_c = sph_coor[atgrid.indices[5]:atgrid.indices[6]] interp = interpolate_given_splines(result, 6, semi_sph_c[:, 1], semi_sph_c[:, 2], deriv=1) # same result from points and interpolation ref_deriv = self.helper_func_power_deriv( atgrid.points[atgrid.indices[5]:atgrid.indices[6]]) assert_allclose(interp, ref_deriv) # test random x, y, z with fd xyz = np.random.rand(1, 3) xyz /= np.linalg.norm(xyz, axis=-1)[:, None] rad = np.random.normal() * np.random.randint(1, 11) xyz *= rad ref_value = self.helper_func_power_deriv(xyz) r = np.linalg.norm(xyz, axis=-1) theta = np.arctan2(xyz[:, 1], xyz[:, 0]) phi = np.arccos(xyz[:, 2] / r) interp = interpolate_given_splines(result, np.abs(rad), theta, phi, deriv=1) assert_allclose(interp, ref_value) with self.assertRaises(ValueError): interpolate_given_splines(result, 6, semi_sph_c[:, 1], semi_sph_c[:, 2], deriv=4) with self.assertRaises(ValueError): interpolate_given_splines(result, 6, semi_sph_c[:, 1], semi_sph_c[:, 2], deriv=-1)
def GaussLegendre(npoints): """Generate Gauss-Legendre grid. Parameters ---------- npoints : int Number of points in the grid Returns ------- OneDGrid A grid instance with points and weights, [-1, 1] """ points, weights = np.polynomial.legendre.leggauss(npoints) return OneDGrid(points, weights)
def GaussChebyshev(npoints): """Generate Gauss-Legendre grid. Parameters ---------- npoints : int Number of points in the grid Returns ------- OneDGrid A grid instance with points and weights """ points, weights = np.polynomial.chebyshev.chebgauss(npoints) return OneDGrid(points, weights)