From 15e6847b90fa17fc028c620d734d02f2f0cea0c5 Mon Sep 17 00:00:00 2001 From: "Patrick J. Roddy" Date: Wed, 20 Nov 2024 14:17:06 +0000 Subject: [PATCH 01/15] gh-384: rename all internal functions to `trapezoid` over `trapz` (#392) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- glass/core/array.py | 4 ++-- glass/galaxies.py | 4 ++-- glass/observations.py | 4 ++-- glass/points.py | 4 ++-- tests/core/test_array.py | 32 ++++++++++++++++---------------- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/glass/core/array.py b/glass/core/array.py index 4b4f43ea..7a7dc094 100644 --- a/glass/core/array.py +++ b/glass/core/array.py @@ -131,7 +131,7 @@ def ndinterp( # noqa: PLR0913 ) -def trapz_product( +def trapezoid_product( f: tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], *ff: tuple[ npt.NDArray[np.float64], @@ -169,7 +169,7 @@ def trapz_product( return np.trapezoid(y, x, axis=axis) # type: ignore[no-any-return] -def cumtrapz( +def cumulative_trapezoid( f: npt.NDArray[np.int_] | npt.NDArray[np.float64], x: npt.NDArray[np.int_] | npt.NDArray[np.float64], dtype: npt.DTypeLike | None = None, diff --git a/glass/galaxies.py b/glass/galaxies.py index 4d14af49..327fc1de 100644 --- a/glass/galaxies.py +++ b/glass/galaxies.py @@ -26,7 +26,7 @@ import numpy as np import numpy.typing as npt -from glass.core.array import broadcast_leading_axes, cumtrapz +from glass.core.array import broadcast_leading_axes, cumulative_trapezoid if typing.TYPE_CHECKING: from cosmology import Cosmology @@ -129,7 +129,7 @@ def redshifts_from_nz( # go through extra dimensions; also works if dims is empty for k in np.ndindex(dims): # compute the CDF of each galaxy population - cdf = cumtrapz(nz_out[k], z_out[k], dtype=float) + cdf = cumulative_trapezoid(nz_out[k], z_out[k], dtype=float) cdf /= cdf[-1] # sample redshifts and store result diff --git a/glass/observations.py b/glass/observations.py index cfa95c7f..dbb601a5 100644 --- a/glass/observations.py +++ b/glass/observations.py @@ -34,7 +34,7 @@ import numpy as np import numpy.typing as npt -from glass.core.array import cumtrapz +from glass.core.array import cumulative_trapezoid def vmap_galactic_ecliptic( @@ -263,7 +263,7 @@ def equal_dens_zbins( # first compute the cumulative integral (by trapezoidal rule) # then normalise: the first z is at CDF = 0, the last z at CDF = 1 # interpolate to find the z values at CDF = i/nbins for i = 0, ..., nbins - cuml_nz = cumtrapz(nz, z) + cuml_nz = cumulative_trapezoid(nz, z) cuml_nz /= cuml_nz[[-1]] zbinedges = np.interp(np.linspace(0, 1, nbins + 1), cuml_nz, z) diff --git a/glass/points.py b/glass/points.py index b35f4bd1..39f1cf23 100644 --- a/glass/points.py +++ b/glass/points.py @@ -37,7 +37,7 @@ import numpy as np import numpy.typing as npt -from glass.core.array import broadcast_first, broadcast_leading_axes, trapz_product +from glass.core.array import broadcast_first, broadcast_leading_axes, trapezoid_product if typing.TYPE_CHECKING: import collections.abc @@ -85,7 +85,7 @@ def effective_bias( """ norm = np.trapezoid(w.wa, w.za) - return trapz_product((z, bz), (w.za, w.wa)) / norm + return trapezoid_product((z, bz), (w.za, w.wa)) / norm def linear_bias( diff --git a/tests/core/test_array.py b/tests/core/test_array.py index 8258b75e..4dd5973c 100644 --- a/tests/core/test_array.py +++ b/tests/core/test_array.py @@ -7,9 +7,9 @@ from glass.core.array import ( broadcast_first, broadcast_leading_axes, - cumtrapz, + cumulative_trapezoid, ndinterp, - trapz_product, + trapezoid_product, ) # check if scipy is available for testing @@ -144,21 +144,21 @@ def test_ndinterp() -> None: ) -def test_trapz_product() -> None: +def test_trapezoid_product() -> None: x1 = np.linspace(0, 2, 100) f1 = np.full_like(x1, 2.0) x2 = np.linspace(1, 2, 10) f2 = np.full_like(x2, 0.5) - s = trapz_product((x1, f1), (x2, f2)) + s = trapezoid_product((x1, f1), (x2, f2)) np.testing.assert_allclose(s, 1.0) @pytest.mark.skipif(not HAVE_SCIPY, reason="test requires SciPy") -def test_cumtrapz() -> None: - from scipy.integrate import cumulative_trapezoid +def test_cumulative_trapezoid() -> None: + import scipy.integrate as spi # 1D f and x @@ -167,19 +167,19 @@ def test_cumtrapz() -> None: # default dtype (int - not supported by scipy) - glass_ct = cumtrapz(f, x) + glass_ct = cumulative_trapezoid(f, x) np.testing.assert_allclose(glass_ct, np.array([0, 1, 4, 7])) # explicit dtype (float) - glass_ct = cumtrapz(f, x, dtype=float) - scipy_ct = cumulative_trapezoid(f, x, initial=0) + glass_ct = cumulative_trapezoid(f, x, dtype=float) + scipy_ct = spi.cumulative_trapezoid(f, x, initial=0) np.testing.assert_allclose(glass_ct, scipy_ct) # explicit return array - result = cumtrapz(f, x, dtype=float, out=np.zeros((4,))) - scipy_ct = cumulative_trapezoid(f, x, initial=0) + result = cumulative_trapezoid(f, x, dtype=float, out=np.zeros((4,))) + scipy_ct = spi.cumulative_trapezoid(f, x, initial=0) np.testing.assert_allclose(result, scipy_ct) # 2D f and 1D x @@ -189,17 +189,17 @@ def test_cumtrapz() -> None: # default dtype (int - not supported by scipy) - glass_ct = cumtrapz(f, x) + glass_ct = cumulative_trapezoid(f, x) np.testing.assert_allclose(glass_ct, np.array([[0, 2, 12, 31], [0, 2, 8, 17]])) # explicit dtype (float) - glass_ct = cumtrapz(f, x, dtype=float) - scipy_ct = cumulative_trapezoid(f, x, initial=0) + glass_ct = cumulative_trapezoid(f, x, dtype=float) + scipy_ct = spi.cumulative_trapezoid(f, x, initial=0) np.testing.assert_allclose(glass_ct, scipy_ct) # explicit return array - glass_ct = cumtrapz(f, x, dtype=float, out=np.zeros((2, 4))) - scipy_ct = cumulative_trapezoid(f, x, initial=0) + glass_ct = cumulative_trapezoid(f, x, dtype=float, out=np.zeros((2, 4))) + scipy_ct = spi.cumulative_trapezoid(f, x, initial=0) np.testing.assert_allclose(glass_ct, scipy_ct) From 571d3a7fc4d58bc2291cb4b01cd1e00ff43c12a9 Mon Sep 17 00:00:00 2001 From: "Patrick J. Roddy" Date: Thu, 21 Nov 2024 09:28:07 +0000 Subject: [PATCH 02/15] gh-443: `if` should be `elif` in `fixed_zbins` (#444) --- glass/observations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glass/observations.py b/glass/observations.py index dbb601a5..5ac8a982 100644 --- a/glass/observations.py +++ b/glass/observations.py @@ -225,7 +225,7 @@ def fixed_zbins( """ if nbins is not None and dz is None: zbinedges = np.linspace(zmin, zmax, nbins + 1) - if nbins is None and dz is not None: + elif nbins is None and dz is not None: zbinedges = np.arange(zmin, zmax, dz) else: msg = "exactly one of nbins and dz must be given" From 09b14bdaa30066d159e24110f10ae594dcfbd361 Mon Sep 17 00:00:00 2001 From: "Patrick J. Roddy" Date: Thu, 21 Nov 2024 10:47:31 +0000 Subject: [PATCH 03/15] gh-445: place all public function in `__all__` (#446) --- glass/__init__.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/glass/__init__.py b/glass/__init__.py index 5dfcf0ad..6015ea27 100644 --- a/glass/__init__.py +++ b/glass/__init__.py @@ -1,5 +1,60 @@ """GLASS package.""" +__all__ = [ + "cls2cov", + "combine", + "cubic_windows", + "deflect", + "density_weight", + "discretized_cls", + "distance_grid", + "distance_weight", + "effective_bias", + "effective_cls", + "ellipticity_gaussian", + "ellipticity_intnorm", + "ellipticity_ryden04", + "equal_dens_zbins", + "fixed_zbins", + "from_convergence", + "galaxy_shear", + "gaussian_nz", + "gaussian_phz", + "generate_gaussian", + "generate_lognormal", + "getcl", + "iternorm", + "linear_bias", + "linear_windows", + "load_cls", + "loglinear_bias", + "lognormal_gls", + "multalm", + "multi_plane_matrix", + "multi_plane_weights", + "MultiPlaneConvergence", + "partition", + "position_weights", + "positions_from_delta", + "RadialWindow", + "redshift_grid", + "redshifts", + "redshifts_from_nz", + "restrict", + "save_cls", + "shear_from_convergence", + "smail_nz", + "tomo_nz_gausserr", + "tophat_windows", + "transform_cls", + "triaxial_axis_ratio", + "uniform_positions", + "vmap_galactic_ecliptic", + "volume_weight", + "write_catalog", +] + + import contextlib from importlib.metadata import PackageNotFoundError @@ -19,7 +74,6 @@ transform_cls, ) from glass.galaxies import ( - _kappa_ia_nla, galaxy_shear, gaussian_phz, redshifts, From d5f4fd8b81dc867edfaf998105db6baadcb078e7 Mon Sep 17 00:00:00 2001 From: "Patrick J. Roddy" Date: Thu, 21 Nov 2024 11:43:53 +0000 Subject: [PATCH 04/15] gh-441: import directly from `glass` in tests (#442) --- tests/conftest.py | 2 -- tests/test_fields.py | 2 +- tests/test_fits.py | 6 +++--- tests/test_galaxies.py | 7 ++++++- tests/test_lensing.py | 4 ++-- tests/test_points.py | 2 +- tests/test_shapes.py | 2 +- tests/test_shells.py | 7 ++++++- 8 files changed, 20 insertions(+), 12 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index b2b6a9fd..43489682 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,6 +4,4 @@ @pytest.fixture(scope="session") def rng() -> np.random.Generator: - import numpy as np - return np.random.default_rng(seed=42) diff --git a/tests/test_fields.py b/tests/test_fields.py index 46b32843..9f33daee 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1,6 +1,6 @@ import numpy as np -from glass.fields import getcl +from glass import getcl def test_getcl() -> None: diff --git a/tests/test_fits.py b/tests/test_fits.py index bfc57599..f2a21186 100644 --- a/tests/test_fits.py +++ b/tests/test_fits.py @@ -5,7 +5,7 @@ import numpy.typing as npt import pytest -from glass import user +from glass import write_catalog # check if fitsio is available for testing HAVE_FITSIO = importlib.util.find_spec("fitsio") is not None @@ -37,7 +37,7 @@ def _test_append( hdu.write(data, names=names, firstrow=hdu.get_nrows()) with ( - user.write_catalog(tmp_path / filename_gfits, ext="CATALOG") as out, + write_catalog(tmp_path / filename_gfits, ext="CATALOG") as out, fitsio.FITS(tmp_path / filename_tfits, "rw", clobber=True) as my_fits, ): for i in range(my_max): @@ -80,7 +80,7 @@ def raise_error(msg: str) -> None: raise TestWriteError(msg) try: - with user.write_catalog(tmp_path / filename, ext="CATALOG") as out: + with write_catalog(tmp_path / filename, ext="CATALOG") as out: for i in range(my_max): if i == except_int: msg = "Unhandled exception" diff --git a/tests/test_galaxies.py b/tests/test_galaxies.py index a2a347eb..97a73767 100644 --- a/tests/test_galaxies.py +++ b/tests/test_galaxies.py @@ -3,7 +3,12 @@ import pytest import pytest_mock -from glass.galaxies import galaxy_shear, gaussian_phz, redshifts, redshifts_from_nz +from glass import ( + galaxy_shear, + gaussian_phz, + redshifts, + redshifts_from_nz, +) def test_redshifts(mocker: pytest_mock.MockerFixture) -> None: diff --git a/tests/test_lensing.py b/tests/test_lensing.py index 48a149d1..d0c2fb72 100644 --- a/tests/test_lensing.py +++ b/tests/test_lensing.py @@ -7,13 +7,13 @@ import numpy.typing as npt import pytest -from glass.lensing import ( +from glass import ( MultiPlaneConvergence, + RadialWindow, deflect, multi_plane_matrix, multi_plane_weights, ) -from glass.shells import RadialWindow if typing.TYPE_CHECKING: from cosmology import Cosmology diff --git a/tests/test_points.py b/tests/test_points.py index a4e545ab..cd7dced2 100644 --- a/tests/test_points.py +++ b/tests/test_points.py @@ -6,7 +6,7 @@ import numpy.typing as npt import pytest -from glass.points import ( +from glass import ( effective_bias, linear_bias, loglinear_bias, diff --git a/tests/test_shapes.py b/tests/test_shapes.py index e965fdce..71892455 100644 --- a/tests/test_shapes.py +++ b/tests/test_shapes.py @@ -1,7 +1,7 @@ import numpy as np import pytest -from glass.shapes import ( +from glass import ( ellipticity_gaussian, ellipticity_intnorm, ellipticity_ryden04, diff --git a/tests/test_shells.py b/tests/test_shells.py index 95ba967c..33259bcf 100644 --- a/tests/test_shells.py +++ b/tests/test_shells.py @@ -1,7 +1,12 @@ import numpy as np import pytest -from glass.shells import RadialWindow, partition, restrict, tophat_windows +from glass import ( + RadialWindow, + partition, + restrict, + tophat_windows, +) def test_tophat_windows() -> None: From 6fbeb97d1def2196f2309253bf446e361300a147 Mon Sep 17 00:00:00 2001 From: "Patrick J. Roddy" Date: Thu, 21 Nov 2024 12:07:55 +0000 Subject: [PATCH 05/15] gh-447: make sure `dz` gives the same results as `nbins` (#448) --- glass/observations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glass/observations.py b/glass/observations.py index 5ac8a982..a7802364 100644 --- a/glass/observations.py +++ b/glass/observations.py @@ -226,7 +226,7 @@ def fixed_zbins( if nbins is not None and dz is None: zbinedges = np.linspace(zmin, zmax, nbins + 1) elif nbins is None and dz is not None: - zbinedges = np.arange(zmin, zmax, dz) + zbinedges = np.arange(zmin, np.nextafter(zmax + dz, zmax), dz) else: msg = "exactly one of nbins and dz must be given" raise ValueError(msg) From 33fc30ec25357184c806e4c7f21156cf0bb3fc45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:08:47 +0000 Subject: [PATCH 06/15] DEV: Bump thehanimo/pr-title-checker from 1.4.2 to 1.4.3 (#452) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 1b78225f..deb17d48 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Enfore PR title format - uses: thehanimo/pr-title-checker@v1.4.2 + uses: thehanimo/pr-title-checker@v1.4.3 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} pass_on_octokit_error: false From 385e745b4744c846e143b1bd50b4a2c11ff83fdf Mon Sep 17 00:00:00 2001 From: "Patrick J. Roddy" Date: Mon, 25 Nov 2024 10:09:38 +0000 Subject: [PATCH 07/15] gh-450: move all `pytest.fixture` into `conftest.py` (#451) --- tests/conftest.py | 38 ++++++++++++++++++++++++++++++++++++++ tests/test_lensing.py | 34 ---------------------------------- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 43489682..ab4ae201 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,45 @@ import numpy as np +import numpy.typing as npt import pytest +from cosmology import Cosmology + +from glass import RadialWindow + + +@pytest.fixture(scope="session") +def cosmo() -> Cosmology: + class MockCosmology: + @property + def omega_m(self) -> float: + return 0.3 + + def ef(self, z: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]: + return (self.omega_m * (1 + z) ** 3 + 1 - self.omega_m) ** 0.5 + + def xm( + self, + z: npt.NDArray[np.float64], + z2: npt.NDArray[np.float64] | None = None, + ) -> npt.NDArray[np.float64]: + if z2 is None: + return np.array(z) * 1000 + return (np.array(z2) - np.array(z)) * 1000 + + return MockCosmology() + @pytest.fixture(scope="session") def rng() -> np.random.Generator: return np.random.default_rng(seed=42) + + +@pytest.fixture(scope="session") +def shells() -> list[RadialWindow]: + return [ + RadialWindow(np.array([0.0, 1.0, 2.0]), np.array([0.0, 1.0, 0.0]), 1.0), + RadialWindow(np.array([1.0, 2.0, 3.0]), np.array([0.0, 1.0, 0.0]), 2.0), + RadialWindow(np.array([2.0, 3.0, 4.0]), np.array([0.0, 1.0, 0.0]), 3.0), + RadialWindow(np.array([3.0, 4.0, 5.0]), np.array([0.0, 1.0, 0.0]), 4.0), + RadialWindow(np.array([4.0, 5.0, 6.0]), np.array([0.0, 1.0, 0.0]), 5.0), + ] diff --git a/tests/test_lensing.py b/tests/test_lensing.py index d0c2fb72..cdb343f5 100644 --- a/tests/test_lensing.py +++ b/tests/test_lensing.py @@ -4,7 +4,6 @@ import healpix import numpy as np -import numpy.typing as npt import pytest from glass import ( @@ -19,39 +18,6 @@ from cosmology import Cosmology -@pytest.fixture -def shells() -> list[RadialWindow]: - return [ - RadialWindow(np.array([0.0, 1.0, 2.0]), np.array([0.0, 1.0, 0.0]), 1.0), - RadialWindow(np.array([1.0, 2.0, 3.0]), np.array([0.0, 1.0, 0.0]), 2.0), - RadialWindow(np.array([2.0, 3.0, 4.0]), np.array([0.0, 1.0, 0.0]), 3.0), - RadialWindow(np.array([3.0, 4.0, 5.0]), np.array([0.0, 1.0, 0.0]), 4.0), - RadialWindow(np.array([4.0, 5.0, 6.0]), np.array([0.0, 1.0, 0.0]), 5.0), - ] - - -@pytest.fixture -def cosmo() -> Cosmology: - class MockCosmology: - @property - def omega_m(self) -> float: - return 0.3 - - def ef(self, z: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]: - return (self.omega_m * (1 + z) ** 3 + 1 - self.omega_m) ** 0.5 - - def xm( - self, - z: npt.NDArray[np.float64], - z2: npt.NDArray[np.float64] | None = None, - ) -> npt.NDArray[np.float64]: - if z2 is None: - return np.array(z) * 1000 - return (np.array(z2) - np.array(z)) * 1000 - - return MockCosmology() - - @pytest.mark.parametrize("usecomplex", [True, False]) def test_deflect_nsew(usecomplex: bool) -> None: # noqa: FBT001 d = 5.0 From ca32a635d181c32ec710b1c9ebf8f61052389e27 Mon Sep 17 00:00:00 2001 From: Saransh Chopra Date: Mon, 25 Nov 2024 21:49:11 +0530 Subject: [PATCH 08/15] gh-453: don't run PR title checker on dependabot PRs (#455) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/pull_request.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index deb17d48..fefd79f8 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -12,7 +12,8 @@ on: jobs: check-pr-title: - if: github.actor != 'pre-commit-ci[bot]' + if: + github.actor != 'pre-commit-ci[bot]' && github.actor != 'dependabot[bot]' runs-on: ubuntu-latest steps: - name: Enfore PR title format From ea3cb74fad0fb2e0e3ec9daf69ac2eacc3ce97e5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:03:14 +0000 Subject: [PATCH 09/15] pre-commit.ci: update pre-commit hooks (#458) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- glass/__init__.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6cb50318..dd534efd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,7 +33,7 @@ repos: args: - --strict - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.4 + rev: v0.8.0 hooks: - id: ruff - id: ruff-format @@ -44,7 +44,7 @@ repos: additional_dependencies: - black - repo: https://github.com/pappasam/toml-sort - rev: v0.24.1 + rev: v0.24.2 hooks: - id: toml-sort-fix - repo: https://github.com/rbubley/mirrors-prettier @@ -52,7 +52,7 @@ repos: hooks: - id: prettier - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.42.0 + rev: v0.43.0 hooks: - id: markdownlint-fix args: diff --git a/glass/__init__.py b/glass/__init__.py index 6015ea27..aa968192 100644 --- a/glass/__init__.py +++ b/glass/__init__.py @@ -1,6 +1,8 @@ """GLASS package.""" __all__ = [ + "MultiPlaneConvergence", + "RadialWindow", "cls2cov", "combine", "cubic_windows", @@ -32,11 +34,9 @@ "multalm", "multi_plane_matrix", "multi_plane_weights", - "MultiPlaneConvergence", "partition", "position_weights", "positions_from_delta", - "RadialWindow", "redshift_grid", "redshifts", "redshifts_from_nz", From 4ef0167843c90eb266c2890836307b23bc185760 Mon Sep 17 00:00:00 2001 From: "Patrick J. Roddy" Date: Tue, 26 Nov 2024 12:26:04 +0000 Subject: [PATCH 10/15] gh-456: call `redshift_grid` within `distance_grid` (#457) Co-authored-by: Nicolas Tessore --- glass/shells.py | 66 ++++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/glass/shells.py b/glass/shells.py index 23925c23..a28a1295 100644 --- a/glass/shells.py +++ b/glass/shells.py @@ -708,6 +708,45 @@ def partition_restrict( return part +def _uniform_grid( + start: float, + stop: float, + *, + step: float | None = None, + num: int | None = None, +) -> npt.NDArray[np.float64]: + """ + Create a uniform grid. + + Parameters + ---------- + start + The minimum value. + stop + The maximum value. + step + The spacing. + num + The number of samples. + + Returns + ------- + The uniform grid. + + Raises + ------ + ValueError + If both ``step`` and ``num`` are given. + + """ + if step is not None and num is None: + return np.arange(start, np.nextafter(stop + step, stop), step) + if step is None and num is not None: + return np.linspace(start, stop, num + 1) + msg = "exactly one of grid step size or number of steps must be given" + raise ValueError(msg) + + def redshift_grid( zmin: float, zmax: float, @@ -733,20 +772,8 @@ def redshift_grid( ------- The redshift grid. - Raises - ------ - ValueError - If both ``dz`` and ``num`` are given. - """ - if dz is not None and num is None: - z = np.arange(zmin, np.nextafter(zmax + dz, zmax), dz) - elif dz is None and num is not None: - z = np.linspace(zmin, zmax, num + 1) - else: - msg = 'exactly one of "dz" or "num" must be given' - raise ValueError(msg) - return z + return _uniform_grid(zmin, zmax, step=dz, num=num) def distance_grid( @@ -777,20 +804,9 @@ def distance_grid( ------- The redshift grid. - Raises - ------ - ValueError - If both ``dx`` and ``num`` are given. - """ xmin, xmax = cosmo.dc(zmin), cosmo.dc(zmax) - if dx is not None and num is None: - x = np.arange(xmin, np.nextafter(xmax + dx, xmax), dx) - elif dx is None and num is not None: - x = np.linspace(xmin, xmax, num + 1) - else: - msg = 'exactly one of "dx" or "num" must be given' - raise ValueError(msg) + x = _uniform_grid(xmin, xmax, step=dx, num=num) return cosmo.dc_inv(x) # type: ignore[no-any-return] From efa7d2273c8a4dae3fa2d8c1c672a5cc489e303f Mon Sep 17 00:00:00 2001 From: "Patrick J. Roddy" Date: Wed, 27 Nov 2024 17:42:30 +0000 Subject: [PATCH 11/15] gh-271: add tests for `glass.observations` (#436) --- glass/observations.py | 10 +-- tests/test_observations.py | 144 +++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 tests/test_observations.py diff --git a/glass/observations.py b/glass/observations.py index a7802364..229099eb 100644 --- a/glass/observations.py +++ b/glass/observations.py @@ -89,7 +89,7 @@ def gaussian_nz( mean: float | npt.NDArray[np.float64], sigma: float | npt.NDArray[np.float64], *, - norm: npt.NDArray[np.float64] | None = None, + norm: float | npt.NDArray[np.float64] | None = None, ) -> npt.NDArray[np.float64]: """ Gaussian redshift distribution. @@ -130,11 +130,11 @@ def gaussian_nz( def smail_nz( z: npt.NDArray[np.float64], - z_mode: npt.NDArray[np.float64], - alpha: npt.NDArray[np.float64], - beta: npt.NDArray[np.float64], + z_mode: float | npt.NDArray[np.float64], + alpha: float | npt.NDArray[np.float64], + beta: float | npt.NDArray[np.float64], *, - norm: npt.NDArray[np.float64] | None = None, + norm: float | npt.NDArray[np.float64] | None = None, ) -> npt.NDArray[np.float64]: r""" Redshift distribution following Smail et al. (1994). diff --git a/tests/test_observations.py b/tests/test_observations.py new file mode 100644 index 00000000..d801881f --- /dev/null +++ b/tests/test_observations.py @@ -0,0 +1,144 @@ +import numpy as np +import pytest + +from glass import ( + equal_dens_zbins, + fixed_zbins, + gaussian_nz, + smail_nz, + tomo_nz_gausserr, + vmap_galactic_ecliptic, +) + + +def test_vmap_galactic_ecliptic() -> None: + """Add unit tests for :func:`vmap_galactic_ecliptic`.""" + n_side = 4 + + # check shape + + vmap = vmap_galactic_ecliptic(n_side) + np.testing.assert_array_equal(len(vmap), 12 * n_side**2) + + # no rotation + + vmap = vmap_galactic_ecliptic(n_side, galactic=(0, 0), ecliptic=(0, 0)) + np.testing.assert_array_equal(vmap, np.zeros_like(vmap)) + + # check errors raised + + with pytest.raises(TypeError, match="galactic stripe must be a pair of numbers"): + vmap_galactic_ecliptic(n_side, galactic=(1,)) # type: ignore[arg-type] + + with pytest.raises(TypeError, match="ecliptic stripe must be a pair of numbers"): + vmap_galactic_ecliptic(n_side, ecliptic=(1,)) # type: ignore[arg-type] + + with pytest.raises(TypeError, match="galactic stripe must be a pair of numbers"): + vmap_galactic_ecliptic(n_side, galactic=(1, 2, 3)) # type: ignore[arg-type] + + with pytest.raises(TypeError, match="ecliptic stripe must be a pair of numbers"): + vmap_galactic_ecliptic(n_side, ecliptic=(1, 2, 3)) # type: ignore[arg-type] + + +def test_gaussian_nz(rng: np.random.Generator) -> None: + """Add unit tests for :func:`gaussian_nz`.""" + mean = 0 + sigma = 1 + z = np.linspace(0, 1, 11) + + # check passing in the norm + + nz = gaussian_nz(z, mean, sigma, norm=0) + np.testing.assert_array_equal(nz, np.zeros_like(nz)) + + # check the value of each entry is close to the norm + + norm = 1 + nz = gaussian_nz(z, mean, sigma, norm=norm) + np.testing.assert_allclose(nz.sum() / nz.shape, norm, rtol=1e-2) + + # check multidimensionality size + + nz = gaussian_nz( + z, + np.tile(mean, z.shape), + np.tile(sigma, z.shape), + norm=rng.normal(size=z.shape), + ) + np.testing.assert_array_equal(nz.shape, (len(z), len(z))) + + +def test_smail_nz() -> None: + """Add unit tests for :func:`smail_nz`.""" + alpha = 1 + beta = 1 + mode = 1 + z = np.linspace(0, 1, 11) + + # check passing in the norm + + pz = smail_nz(z, mode, alpha, beta, norm=0) + np.testing.assert_array_equal(pz, np.zeros_like(pz)) + + +def test_fixed_zbins() -> None: + """Add unit tests for :func:`fixed_zbins`.""" + zmin = 0 + zmax = 1 + + # check nbins input + + nbins = 5 + expected_zbins = [(0.0, 0.2), (0.2, 0.4), (0.4, 0.6), (0.6, 0.8), (0.8, 1.0)] + zbins = fixed_zbins(zmin, zmax, nbins=nbins) + np.testing.assert_array_equal(len(zbins), nbins) + np.testing.assert_allclose(zbins, expected_zbins, rtol=1e-15) + + # check dz input + + dz = 0.2 + zbins = fixed_zbins(zmin, zmax, dz=dz) + np.testing.assert_array_equal(len(zbins), np.ceil((zmax - zmin) / dz)) + np.testing.assert_allclose(zbins, expected_zbins, rtol=1e-15) + + # check dz for spacing which results in a max value above zmax + + zbins = fixed_zbins(zmin, zmax, dz=0.3) + np.testing.assert_array_less(zmax, zbins[-1][1]) + + # check error raised + + with pytest.raises(ValueError, match="exactly one of nbins and dz must be given"): + fixed_zbins(zmin, zmax, nbins=nbins, dz=dz) + + +def test_equal_dens_zbins() -> None: + """Add unit tests for :func:`equal_dens_zbins`.""" + z = np.linspace(0, 1, 11) + nbins = 5 + + # check expected zbins returned + + expected_zbins = [(0.0, 0.2), (0.2, 0.4), (0.4, 0.6), (0.6, 0.8), (0.8, 1.0)] + zbins = equal_dens_zbins(z, np.ones_like(z), nbins) + np.testing.assert_allclose(zbins, expected_zbins, rtol=1e-15) + + # check output shape + + np.testing.assert_array_equal(len(zbins), nbins) + + +def test_tomo_nz_gausserr() -> None: + """Add unit tests for :func:`tomo_nz_gausserr`.""" + sigma_0 = 0.1 + z = np.linspace(0, 1, 11) + zbins = [(0, 0.2), (0.2, 0.4), (0.4, 0.6), (0.6, 0.8), (0.8, 1.0)] + + # check zeros returned + + binned_nz = tomo_nz_gausserr(z, np.zeros_like(z), sigma_0, zbins) + np.testing.assert_array_equal(binned_nz, np.zeros_like(binned_nz)) + + # check the shape of the output + + np.testing.assert_array_equal(binned_nz.shape, (len(zbins), len(z))) From 6ce96297fa6c28c47a6492ece305c0589b124cea Mon Sep 17 00:00:00 2001 From: Saransh Chopra Date: Wed, 27 Nov 2024 23:13:26 +0530 Subject: [PATCH 12/15] gh-269: add tests for glass.fields (#374) Co-authored-by: Patrick J. Roddy Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- tests/test_fields.py | 400 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 399 insertions(+), 1 deletion(-) diff --git a/tests/test_fields.py b/tests/test_fields.py index 9f33daee..11f322d5 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1,6 +1,392 @@ +import healpy as hp import numpy as np +import pytest -from glass import getcl +from glass import ( + cls2cov, + discretized_cls, + effective_cls, + generate_gaussian, + generate_lognormal, + getcl, + iternorm, + lognormal_gls, + multalm, + transform_cls, +) + + +def test_iternorm() -> None: + # check output shapes and types + + k = 2 + + generator = iternorm(k, np.array([1.0, 0.5, 0.5, 0.5, 0.2, 0.1, 0.5, 0.1, 0.2])) + result = next(generator) + + j, a, s = result + + assert isinstance(j, int) + assert a.shape == (k,) + assert isinstance(s, float) # type: ignore[unreachable] + assert s.shape == () # type: ignore[unreachable] + + # specify size + + size = 3 + + generator = iternorm( + k, + np.array( + [ + [1.0, 0.5, 0.5], + [0.5, 0.2, 0.1], + [0.5, 0.1, 0.2], + ] + ), + size, + ) + result = next(generator) + + j, a, s = result + + assert isinstance(j, int) + assert a.shape == (size, k) + assert s.shape == (size,) + + # test shape mismatch error + + with pytest.raises(TypeError, match="covariance row 0: shape"): + list(iternorm(k, np.array([[1.0, 0.5], [0.5, 0.2]]))) + + # test positive definite error + + with pytest.raises(ValueError, match="covariance matrix is not positive definite"): + list(iternorm(k, np.array([1.0, 0.5, 0.9, 0.5, 0.2, 0.4, 0.9, 0.4, -1.0]))) + + # test multiple iterations + + size = (3,) + + generator = iternorm( + k, + np.array( + [ + [[1.0, 0.5, 0.5], [0.5, 0.2, 0.1], [0.5, 0.1, 0.2]], + [[2.0, 1.0, 0.8], [1.0, 0.5, 0.3], [0.8, 0.3, 0.6]], + ] + ), + size, + ) + + result1 = next(generator) + result2 = next(generator) + + assert result1 != result2 + assert isinstance(result1, tuple) + assert len(result1) == 3 + assert isinstance(result2, tuple) + assert len(result2) == 3 + + # test k = 0 + + generator = iternorm(0, np.array([1.0])) + + j, a, s = result + + assert j == 1 + assert a.shape == (3, 2) + assert isinstance(s, np.ndarray) + assert s.shape == (3,) + + +def test_cls2cov() -> None: + # check output values and shape + + nl, nf, nc = 3, 2, 2 + + generator = cls2cov( + [np.array([1.0, 0.5, 0.3]), None, np.array([0.7, 0.6, 0.1])], # type: ignore[list-item] + nl, + nf, + nc, + ) + cov = next(generator) + + assert cov.shape == (nl, nc + 1) + + np.testing.assert_array_equal(cov[:, 0], np.array([0.5, 0.25, 0.15])) + np.testing.assert_array_equal(cov[:, 1], 0) + np.testing.assert_array_equal(cov[:, 2], 0) + + # test negative value error + + generator = cls2cov( + np.array( # type: ignore[arg-type] + [ + [-1.0, 0.5, 0.3], + [0.8, 0.4, 0.2], + [0.7, 0.6, 0.1], + ] + ), + nl, + nf, + nc, + ) + with pytest.raises(ValueError, match="negative values in cl"): + next(generator) + + # test multiple iterations + + nl, nf, nc = 3, 3, 2 + + generator = cls2cov( + np.array( # type: ignore[arg-type] + [ + [1.0, 0.5, 0.3], + [0.8, 0.4, 0.2], + [0.7, 0.6, 0.1], + [0.9, 0.5, 0.3], + [0.6, 0.3, 0.2], + [0.8, 0.7, 0.4], + ] + ), + nl, + nf, + nc, + ) + + cov1 = np.copy(next(generator)) + cov2 = np.copy(next(generator)) + cov3 = next(generator) + + assert cov1.shape == (nl, nc + 1) + assert cov2.shape == (nl, nc + 1) + assert cov3.shape == (nl, nc + 1) + + assert not np.array_equal(cov1, cov2) + assert not np.array_equal(cov2, cov3) + + +def test_multalm() -> None: + # check output values and shapes + + alm = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]) + bl = np.array([2.0, 0.5, 1.0]) + alm_copy = np.copy(alm) + + result = multalm(alm, bl, inplace=True) + + assert np.array_equal(result, alm) # in-place + expected_result = np.array([2.0, 1.0, 3.0, 2.0, 5.0, 6.0]) + np.testing.assert_allclose(result, expected_result) + assert not np.array_equal(alm_copy, result) + + # multiple with 1s + + bl = np.ones(3) + + result = multalm(alm, bl, inplace=False) + np.testing.assert_array_equal(result, alm) + + # multiple with 0s + + bl = np.array([0.0, 1.0, 0.0]) + + result = multalm(alm, bl, inplace=False) + + expected_result = np.array([0.0, 1.0, 0.0, 2.0, 0.0, 0.0]) + np.testing.assert_allclose(result, expected_result) + + # empty arrays + + alm = np.array([]) + bl = np.array([]) + + result = multalm(alm, bl, inplace=False) + np.testing.assert_array_equal(result, alm) + + +def test_transform_cls() -> None: + tfm = "lognormal" + pars = [2] + sub_cls = np.array([1, 2, 3, 4, 5]) + + # empty cls + + assert transform_cls([], tfm, pars) == [] + + # check output shape + + assert len(transform_cls([sub_cls], tfm, pars)) == 1 + assert len(transform_cls([sub_cls], tfm, pars)[0]) == 5 + + assert len(transform_cls([sub_cls, sub_cls], tfm, pars)) == 2 + assert len(transform_cls([sub_cls, sub_cls], tfm, pars)[0]) == 5 + assert len(transform_cls([sub_cls, sub_cls], tfm, pars)[1]) == 5 + + # one sequence of empty cls + + assert transform_cls([[], sub_cls], tfm, pars)[0] == [] + + # monopole behavior + + assert transform_cls([sub_cls, np.linspace(0, 5, 6)], tfm, pars)[1][0] == 0 + + +def test_lognormal_gls() -> None: + shift = 2 + + # empty cls + + assert lognormal_gls([], shift) == [] + + # check output shape + + assert len(lognormal_gls([np.linspace(1, 5, 5)], shift)) == 1 + assert len(lognormal_gls([np.linspace(1, 5, 5)], shift)[0]) == 5 + + assert len(lognormal_gls([np.linspace(1, 5, 5), np.linspace(1, 5, 5)], shift)) == 2 + assert ( + len(lognormal_gls([np.linspace(1, 5, 5), np.linspace(1, 5, 5)], shift)[0]) == 5 + ) + assert ( + len(lognormal_gls([np.linspace(1, 5, 5), np.linspace(1, 5, 5)], shift)[1]) == 5 + ) + + +def test_discretized_cls() -> None: + # empty cls + + result = discretized_cls([]) + assert result == [] + + # power spectra truncated at lmax + 1 if lmax provided + + result = discretized_cls([np.arange(10), np.arange(10), np.arange(10)], lmax=5) + + for cl in result: + assert len(cl) == 6 + + # check ValueError for triangle number + + with pytest.raises( + ValueError, match="length of cls array is not a triangle number" + ): + discretized_cls([np.arange(10), np.arange(10)], ncorr=1) + + # ncorr not None + + cls = [np.arange(10), np.arange(10), np.arange(10)] + ncorr = 0 + result = discretized_cls(cls, ncorr=ncorr) + + assert len(result[0]) == 10 + assert len(result[1]) == 10 + assert len(result[2]) == 0 # third correlation should be removed + + # check if pixel window function was applied correctly with nside not None + + nside = 4 + + pw = hp.pixwin(nside, lmax=7) + + result = discretized_cls([[], np.ones(10), np.ones(10)], nside=nside) + + for cl in result: + n = min(len(cl), len(pw)) + expected = np.ones(n) * pw[:n] ** 2 + np.testing.assert_allclose(cl[:n], expected) + + +def test_effective_cls() -> None: + # empty cls + + result = effective_cls([], np.array([])) + assert result.shape == (0,) + + # check ValueError for triangle number + + with pytest.raises(ValueError, match="length of cls is not a triangle number"): + effective_cls([np.arange(10), np.arange(10)], np.ones((2, 1))) + + # check ValueError for triangle number + + with pytest.raises(ValueError, match="shape mismatch between fields and weights1"): + effective_cls([], np.ones((3, 1))) + + # check with only weights1 + + cls = [np.arange(15), np.arange(15), np.arange(15)] + weights1 = np.ones((2, 1)) + + result = effective_cls(cls, weights1) + assert result.shape == (1, 1, 15) + + # check truncation if lmax provided + + result = effective_cls(cls, weights1, lmax=5) + + assert result.shape == (1, 1, 6) + np.testing.assert_allclose(result[..., 6:], 0) + + # check with weights1 and weights2 and weights1 is weights2 + + result = effective_cls(cls, weights1, weights2=weights1) + assert result.shape == (1, 1, 15) + + +def test_generate_gaussian() -> None: + gls = [np.array([1.0, 0.5, 0.1])] + nside = 4 + ncorr = 1 + + gaussian_fields = list(generate_gaussian(gls, nside)) + + assert gaussian_fields[0].shape == (hp.nside2npix(nside),) + + # requires resetting the RNG for reproducibility + rng = np.random.default_rng(seed=42) + gaussian_fields = list(generate_gaussian(gls, nside, rng=rng)) + + assert gaussian_fields[0].shape == (hp.nside2npix(nside),) + + # requires resetting the RNG for reproducibility + rng = np.random.default_rng(seed=42) + new_gaussian_fields = list(generate_gaussian(gls, nside, ncorr=ncorr, rng=rng)) + + assert new_gaussian_fields[0].shape == (hp.nside2npix(nside),) + + np.testing.assert_allclose(new_gaussian_fields[0], gaussian_fields[0]) + + with pytest.raises(ValueError, match="all gls are empty"): + list(generate_gaussian([], nside)) + + +def test_generate_lognormal(rng: np.random.Generator) -> None: + gls = [np.array([1.0, 0.5, 0.1])] + nside = 4 + + # check shape and values + + lognormal_fields = list(generate_lognormal(gls, nside, shift=1.0, rng=rng)) + + assert len(lognormal_fields) == len(gls) + for m in lognormal_fields: + assert m.shape == (192,) + assert np.all(m >= -1) + + # check shift + + # requires resetting the RNG to obtain exact the same result multiplied by 2 (shift) + rng = np.random.default_rng(seed=42) + lognormal_fields = list(generate_lognormal(gls, nside, shift=1.0, rng=rng)) + + rng = np.random.default_rng(seed=42) + new_lognormal_fields = list(generate_lognormal(gls, nside, shift=2.0, rng=rng)) + + for ms, mu in zip(new_lognormal_fields, lognormal_fields, strict=False): + np.testing.assert_allclose(ms, mu * 2.0) def test_getcl() -> None: @@ -14,3 +400,15 @@ def test_getcl() -> None: result = getcl(cls, i, j) expected = np.array([min(i, j), max(i, j)], dtype=np.float64) np.testing.assert_array_equal(np.sort(result), expected) + + # check slicing + result = getcl(cls, i, j, lmax=0) + expected = np.array([max(i, j)], dtype=np.float64) + assert len(result) == 1 + np.testing.assert_array_equal(result, expected) + + # check padding + result = getcl(cls, i, j, lmax=50) + expected = np.zeros((49,), dtype=np.float64) + assert len(result) == 51 + np.testing.assert_array_equal(result[2:], expected) From 8d98a138ce78ae1ee9e984d68b46fc2f2b4bbf6e Mon Sep 17 00:00:00 2001 From: Saransh Chopra Date: Wed, 27 Nov 2024 23:14:00 +0530 Subject: [PATCH 13/15] gh-274: add missing tests for `glass.user` (#422) Co-authored-by: Patrick J. Roddy --- tests/{test_fits.py => test_user.py} | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) rename tests/{test_fits.py => test_user.py} (84%) diff --git a/tests/test_fits.py b/tests/test_user.py similarity index 84% rename from tests/test_fits.py rename to tests/test_user.py index f2a21186..dfee8353 100644 --- a/tests/test_fits.py +++ b/tests/test_user.py @@ -5,7 +5,7 @@ import numpy.typing as npt import pytest -from glass import write_catalog +from glass import load_cls, save_cls, write_catalog # check if fitsio is available for testing HAVE_FITSIO = importlib.util.find_spec("fitsio") is not None @@ -14,6 +14,25 @@ my_max = 1000 # Typically number of galaxies in loop except_int = 750 # Where test exception occurs in loop filename = "MyFile.Fits" +cls_file = "Cls.npz" + + +def test_read_write_cls(rng: np.random.Generator, tmp_path: pathlib.Path) -> None: + cls = rng.normal(size=(10, 10)) + save_cls(tmp_path / cls_file, cls) + + assert pathlib.Path.exists(tmp_path / cls_file) + + with np.load(tmp_path / cls_file) as npz: + values = npz["values"] + split = npz["split"] + + np.testing.assert_array_equal(values, np.concatenate(cls)) + np.testing.assert_array_equal(split, np.cumsum([len(cl) for cl in cls[:-1]])) + np.testing.assert_array_equal(cls, np.split(values, split)) + + npz = load_cls(tmp_path / cls_file) + np.testing.assert_array_equal(npz, cls) @pytest.mark.skipif(not HAVE_FITSIO, reason="test requires fitsio") From f1e26d6156c9b32ae051a8b4870c2451b23faa24 Mon Sep 17 00:00:00 2001 From: "Patrick J. Roddy" Date: Wed, 27 Nov 2024 17:44:51 +0000 Subject: [PATCH 14/15] gh-368: add type overloading for `lensing.from_convergence` (#395) --- glass/lensing.py | 115 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/glass/lensing.py b/glass/lensing.py index fbb6e91f..981c025f 100644 --- a/glass/lensing.py +++ b/glass/lensing.py @@ -45,6 +45,117 @@ from glass.shells import RadialWindow +@typing.overload +def from_convergence( + kappa: npt.NDArray[np.float64], + lmax: int | None = None, + *, + potential: typing.Literal[True] = True, + deflection: typing.Literal[False] = False, + shear: typing.Literal[False] = False, + discretized: bool = True, +) -> tuple[npt.NDArray[np.float64]]: + # returns psi + ... + + +@typing.overload +def from_convergence( + kappa: npt.NDArray[np.float64], + lmax: int | None = None, + *, + potential: typing.Literal[False] = False, + deflection: typing.Literal[True] = True, + shear: typing.Literal[False] = False, + discretized: bool = True, +) -> tuple[npt.NDArray[np.complex128]]: + # returns alpha + ... + + +@typing.overload +def from_convergence( + kappa: npt.NDArray[np.float64], + lmax: int | None = None, + *, + potential: typing.Literal[False] = False, + deflection: typing.Literal[False] = False, + shear: typing.Literal[True] = True, + discretized: bool = True, +) -> tuple[npt.NDArray[np.complex128]]: + # returns gamma + ... + + +@typing.overload +def from_convergence( + kappa: npt.NDArray[np.float64], + lmax: int | None = None, + *, + potential: typing.Literal[True] = True, + deflection: typing.Literal[True] = True, + shear: typing.Literal[False] = False, + discretized: bool = True, +) -> tuple[ + npt.NDArray[np.float64], + npt.NDArray[np.complex128], +]: + # returns psi, alpha + ... + + +@typing.overload +def from_convergence( + kappa: npt.NDArray[np.float64], + lmax: int | None = None, + *, + potential: typing.Literal[True] = True, + deflection: typing.Literal[False] = False, + shear: typing.Literal[True] = True, + discretized: bool = True, +) -> tuple[ + npt.NDArray[np.float64], + npt.NDArray[np.complex128], +]: + # returns psi, gamma + ... + + +@typing.overload +def from_convergence( + kappa: npt.NDArray[np.float64], + lmax: int | None = None, + *, + potential: typing.Literal[False] = False, + deflection: typing.Literal[True] = True, + shear: typing.Literal[True] = True, + discretized: bool = True, +) -> tuple[ + npt.NDArray[np.complex128], + npt.NDArray[np.complex128], +]: + # returns alpha, gamma + ... + + +@typing.overload +def from_convergence( + kappa: npt.NDArray[np.float64], + lmax: int | None = None, + *, + potential: typing.Literal[True] = True, + deflection: typing.Literal[True] = True, + shear: typing.Literal[True] = True, + discretized: bool = True, +) -> tuple[ + npt.NDArray[np.float64], + npt.NDArray[np.complex128], + npt.NDArray[np.complex128], +]: + # returns psi, alpha, gamma + ... + + def from_convergence( # noqa: PLR0913 kappa: npt.NDArray[np.float64], lmax: int | None = None, @@ -53,7 +164,7 @@ def from_convergence( # noqa: PLR0913 deflection: bool = False, shear: bool = False, discretized: bool = True, -) -> tuple[npt.NDArray[np.float64], ...]: +) -> tuple[npt.NDArray[np.float64] | npt.NDArray[np.complex128], ...]: r""" Compute other weak lensing maps from the convergence. @@ -175,7 +286,7 @@ def from_convergence( # noqa: PLR0913 ell = np.arange(lmax + 1) # this tuple will be returned - results: tuple[npt.NDArray[np.float64], ...] = () + results: tuple[npt.NDArray[np.float64] | npt.NDArray[np.complex128], ...] = () # convert convergence to potential fl = np.divide(-2, ell * (ell + 1), where=(ell > 0), out=np.zeros(lmax + 1)) From 79ecfc41d56a97af64e18e3021336931eaf0def7 Mon Sep 17 00:00:00 2001 From: "Patrick J. Roddy" Date: Wed, 27 Nov 2024 17:50:56 +0000 Subject: [PATCH 15/15] gh-421: fix TestPyPI deployment (#427) --- .github/workflows/release.yml | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 94ace774..8d9af98b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,14 +2,10 @@ name: Release on: - workflow_dispatch: - inputs: - target: - default: testpypi - description: Deployment target. Can be pypi or testpypi. release: types: - published + workflow_dispatch: jobs: dist: @@ -55,8 +51,16 @@ jobs: needs: dist runs-on: ubuntu-latest environment: - name: publish - url: https://pypi.org/p/glass + name: >- + ${{ (github.event_name == 'release' && + github.event.action == 'published') && + 'publish' || + 'test-publish' }} + url: >- + ${{ (github.event_name == 'release' && + github.event.action == 'published') && + 'https://pypi.org/project/glass' || + 'https://test.pypi.org/project/glass' }} permissions: id-token: write steps: @@ -71,12 +75,11 @@ jobs: - name: Publish to PyPI if: >- - github.event.inputs.target == 'pypi' || (github.event_name == - 'release' && github.event.action == 'published') + github.event_name == 'release' && github.event.action == 'published' uses: pypa/gh-action-pypi-publish@release/v1 - name: Publish to TestPyPI - if: github.event.inputs.target == 'testpypi' + if: github.event_name == 'workflow_dispatch' uses: pypa/gh-action-pypi-publish@release/v1 with: repository-url: https://test.pypi.org/legacy/