class TestLegendreExpansion: """ Testing of spherical harmonics transform for 1D fields. """ def setup_method(self): self.l_max = 2**np.random.randint(4, 8) self.m_max = 0 self.n_lat = 4 * self.l_max self.n_lon = 1 self.sht = SHT(self.l_max, self.m_max, self.n_lon, self.n_lat) self.lat_grid = np.linspace(0, np.pi, self.n_lat + 1) self.lat_grid = 0.5 * (self.lat_grid[1:] + self.lat_grid[:-1]) self.lon_grid = np.linspace(0, 2.0 * np.pi, self.n_lon + 1)[:-1] def test_legendre_transform(self): """ Tests the Legendre expansion by transforming a given Legendre polynomial and ensuring that only one component in the output is set. """ l = np.random.randint(1, self.l_max) m = 0 xx, yy = np.meshgrid(self.lat_grid, self.lon_grid, indexing="xy") zz = sph_harm(m, l, yy, xx) coeffs = self.sht.transform(zz.real) print(coeffs) assert np.sum(np.abs(coeffs) > 1e-6) == 1 def test_legendre_transform_cmplx(self): """ Test transforming from complex spatial field to spectral field and back to ensure that the input field is recovered. """ l = np.random.randint(1, self.l_max) m = 0 xx, yy = np.meshgrid(self.lat_grid, self.lon_grid, indexing="xy") zz = sph_harm(m, l, yy, xx) aa = self.sht.transform_cmplx(zz) zz_rec = self.sht.synthesize_cmplx(2.0 * self.sht.transform_cmplx(zz)) assert np.all(np.isclose(2.0 * zz, zz_rec)) def test_evaluate(self): """ Test that evaluating the spectral representation reproduces the spatial input. """ l = np.random.randint(1, self.l_max) m = 0 xx, yy = np.meshgrid(self.lat_grid, self.lon_grid, indexing="xy") zz_ref = sph_harm(m, l, yy, xx) coeffs = self.sht.transform(zz_ref.real) points = xx.ravel() zz = self.sht.evaluate(coeffs, points) assert np.all(np.isclose(zz, zz_ref.real.ravel()))
class TestSHT: """ Testing of spherical harmonics transform for non-degenrate 2D fields. """ def setup_method(self): self.l_max = np.random.randint(20, 100) self.m_max = np.random.randint(10, self.l_max) self.m_max = self.l_max #np.random.randint(10, self.l_max) self.n_lat = 4 * self.l_max self.n_lon = 4 * self.l_max self.sht = SHT(self.l_max, self.m_max, self.n_lon, self.n_lat) self.lat_grid = np.linspace(0, np.pi, self.n_lat + 1) self.lat_grid = 0.5 * (self.lat_grid[1:] + self.lat_grid[:-1]) self.lon_grid = np.linspace(0, 2.0 * np.pi, self.n_lon + 1)[:-1] def test_latitude_grid(self): """ Test that the SHT class returns the expected latitude grid (Gauss-Legendre). """ lat_grid_ref = self.lat_grid lat_grid = self.sht.get_latitude_grid() assert np.all(np.isclose(lat_grid_ref, lat_grid)) def test_colatitude_grid(self): """ Test that the SHT class returns the expected co-latitude grid (Gauss-Legendre). """ lat_grid_ref = np.cos(self.lat_grid) lat_grid = self.sht.get_colatitude_grid() assert np.all(np.isclose(lat_grid_ref, lat_grid)) def test_spatial_to_spectral(self): """ Test transform from spatial to spectral representation by transforming spherical harmonics and ensuring that the result contains only one significant component. """ l = np.random.randint(1, self.l_max) m_max = min(self.m_max, l) m_max = l m = np.random.randint(-m_max, m_max) xx, yy = np.meshgrid(self.lat_grid, self.lon_grid, indexing="xy") zz = sph_harm(m, l, yy, xx) coeffs = self.sht.transform(zz.real) assert np.sum(np.abs(coeffs) > 1e-6) == 1 def test_inverse_transform(self): """ Test transforming from spatial field to spectral field and back to ensure that the input field is recovered. """ l = np.random.randint(1, self.l_max) m_max = min(self.m_max, l) m_max = l m = np.random.randint(-m_max, m_max) xx, yy = np.meshgrid(self.lon_grid, self.lat_grid, indexing="ij") zz = sph_harm(m, l, xx, yy) coeffs = self.sht.synthesize(self.sht.transform(zz.real).ravel()) assert np.all(np.isclose(zz.real, coeffs)) def test_inverse_transform_cmplx(self): """ Test transforming from spatial field to spectral field and back to ensure that the input field is recovered. """ l = np.random.randint(1, self.l_max) m_max = l #min(self.m_max, l) m = np.random.randint(-m_max, m_max) xx, yy = np.meshgrid(self.lon_grid, self.lat_grid, indexing="ij") zz = sph_harm(m, l, xx, yy) coeffs = self.sht.transform_cmplx(zz) zz_rec = self.sht.synthesize_cmplx(2.0 * coeffs) assert np.all(np.isclose(2.0 * zz, zz_rec)) def test_evaluate(self): """ Test that evaluating the spectral representation reproduces the spatial input. """ l = np.random.randint(1, self.l_max) m_max = min(self.m_max, l) m_max = l m = np.random.randint(-m_max, m_max) xx, yy = np.meshgrid(self.lat_grid, self.lon_grid, indexing="xy") zz_ref = sph_harm(m, l, yy, xx) coeffs = self.sht.transform(zz_ref.real) points = np.stack([yy.ravel(), xx.ravel()], axis=-1) zz = self.sht.evaluate(coeffs, points) assert np.all(np.isclose(zz, zz_ref.real.ravel()))