Skip to content

Commit

Permalink
feat: add union of cones with small cones strategy
Browse files Browse the repository at this point in the history
the idea is that we might add the strategy as an argument later for overlapping cones
  • Loading branch information
ManonMarchand committed Sep 9, 2024
1 parent 30161a1 commit 355c843
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 28 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

* Creation of a MOC from a zone (defined by min/max ra and dec)`MOC.from_zone`
* Creation of a single MOC from a lot of small cones is faster with the new option in
`MOC.from_cones`: the keyword 'union_strategy' can now take the value 'small_cones'.

## [0.16.2]

Expand Down
64 changes: 52 additions & 12 deletions notebooks/01-Creating_MOCs_from_shapes.ipynb

Large diffs are not rendered by default.

71 changes: 55 additions & 16 deletions python/mocpy/moc/moc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1186,9 +1186,9 @@ def from_elliptical_cone(cls, lon, lat, a, b, pa, max_depth, *, delta_depth=2):
Maximum HEALPix cell resolution.
delta_depth : int, optional
To control the approximation, you can choose to perform the computations at a deeper
depth using the `depth_delta` parameter.
depth using the `delta_depth` parameter.
The depth at which the computations will be made will therefore be equal to
`depth` + `depth_delta`.
`depth` + `delta_depth`.
Returns
-------
Expand Down Expand Up @@ -1243,9 +1243,9 @@ def from_cone(cls, lon, lat, radius, max_depth, *, delta_depth=2):
Maximum HEALPix cell resolution.
delta_depth : int, optional
To control the approximation, you can choose to perform the computations at a deeper
depth using the `depth_delta` parameter.
depth using the `delta_depth` parameter.
The depth at which the computations will be made will therefore be equal to
`max_depth` + `depth_delta`.
`max_depth` + `delta_depth`.
Returns
-------
Expand All @@ -1264,6 +1264,11 @@ def from_cone(cls, lon, lat, radius, max_depth, *, delta_depth=2):
... max_depth=10
... )
"""
if len(lon) != 1:
raise ValueError(
"'MOC.from_cone' only works with one cone. To create MOCs "
"from multiple cones, use 'MOC.from_cones'.",
)
index = mocpy.from_cone(
lon[0],
lat[0],
Expand All @@ -1275,7 +1280,17 @@ def from_cone(cls, lon, lat, radius, max_depth, *, delta_depth=2):

@classmethod
@validate_lonlat
def from_cones(cls, lon, lat, radius, max_depth, *, delta_depth=2, n_threads=None):
def from_cones(
cls,
lon,
lat,
radius,
max_depth,
*,
union_strategy=None,
delta_depth=2,
n_threads=None,
):
"""
Create a list of MOCs from cones.
Expand All @@ -1294,31 +1309,55 @@ def from_cones(cls, lon, lat, radius, max_depth, *, delta_depth=2, n_threads=Non
The radius angle of the cone. Can be scalar or a list of radii.
max_depth : int
Maximum HEALPix cell resolution.
union_strategy : str, optional
Return the union of all the cones instead of the list of MOCs. For now, the
"small_cones" strategy is implemented. It works better if the cones don't overlap
a lot.
delta_depth : int, optional
To control the approximation, you can choose to perform the computations at a deeper
depth using the `depth_delta` parameter.
depth using the `delta_depth` parameter.
The depth at which the computations will be made will therefore be equal to
`max_depth` + `depth_delta`.
`max_depth` + `delta_depth`.
n_threads : int, optional
The number of threads to be used. If this is set to None (default value),
all available threads will be used.
Returns
-------
List[`~mocpy.moc.MOC`]
The resulting list of MOCs
List[`~mocpy.moc.MOC`] or `~mocpy.MOC`
The resulting list of MOCs, or if 'union_strategy' is not None, the MOC of the
union of all the cones.
Examples
--------
>>> from mocpy import MOC
>>> import astropy.units as u
>>> moc = MOC.from_cone(
>>> moc = MOC.from_cones(
... lon=[1, 4] * u.deg,
... lat=[2, 5] * u.deg,
... radius=10 * u.deg,
... max_depth=10
... radius=1 * u.arcmin,
... max_depth=12,
... union_strategy="small_cones"
... )
"""
if union_strategy == "small_cones":
if radius.isscalar:
radii = np.full(len(lon), Angle(radius).to_value(u.deg))
else:
radii = Angle(radius).to_value(u.deg)
index = mocpy.from_small_cones(
lon,
lat,
radii,
np.uint8(max_depth),
np.uint8(delta_depth),
n_threads,
)
return cls(index)

if union_strategy is not None:
raise ValueError("'union_strategy' can only be None or 'small_cones'.")

if radius.isscalar:
indices = mocpy.from_same_cones(
lon,
Expand Down Expand Up @@ -1552,9 +1591,9 @@ def from_ring(
Maximum HEALPix cell resolution.
delta_depth : int, optional
To control the approximation, you can choose to perform the computations at a deeper
depth using the `depth_delta` parameter.
depth using the `delta_depth` parameter.
The depth at which the computations will be made will therefore be equal to
`max_depth` + `depth_delta`.
`max_depth` + `delta_depth`.
Returns
-------
Expand Down Expand Up @@ -1772,9 +1811,9 @@ def from_stcs(cls, stcs, max_depth, delta_depth=2):
Maximum HEALPix cell resolution.
delta_depth : int, optional
To control the approximation, you can choose to perform the computations at a deeper
depth using the `depth_delta` parameter.
depth using the `delta_depth` parameter.
The depth at which the computations will be made will therefore be equal to
`max_depth` + `depth_delta`.
`max_depth` + `delta_depth`.
Returns
-------
Expand Down
24 changes: 24 additions & 0 deletions python/mocpy/tests/test_moc.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,11 @@ def test_from_elliptical_cone():
)


def test_from_cone():
with pytest.raises(ValueError, match="'MOC.from_cone' only works with one cone.*"):
MOC.from_cone([2, 4] * u.deg, [5, 6] * u.deg, radius=2 * u.arcmin, max_depth=2)


def test_from_cones():
# same radius
radius = 2 * u.arcmin
Expand All @@ -734,6 +739,14 @@ def test_from_cones():
barycenter.ra,
barycenter.dec,
) < Angle(1 * u.arcsec)
moc = MOC.from_cones(
lon,
lat,
radius=radius,
max_depth=14,
union_strategy="small_cones",
)
assert isinstance(moc, MOC)
# different radii
radii = [5, 6] * u.arcmin
cones = MOC.from_cones(lon, lat, radius=radii, max_depth=14)
Expand All @@ -743,6 +756,17 @@ def test_from_cones():
cone.sky_fraction
> (((radius) ** 2).to(u.steradian) / 4 * u.steradian).value
)
moc = MOC.from_cones(
lon,
lat,
radius=radii,
max_depth=14,
union_strategy="small_cones",
)
assert isinstance(moc, MOC)
# test error
with pytest.raises(ValueError, match="'union_strategy'*"):
MOC.from_cones(lon, lat, radius=radii, max_depth=14, union_strategy="big_cones")


@pytest.fixture()
Expand Down

0 comments on commit 355c843

Please sign in to comment.