Skip to content

Commit e4ab53b

Browse files
authored
Add safeguards to only allow global HEALPix data (#1257)
* add safeguards to non-global HEALPix data * use more robust method and add better docstrings
1 parent 9fc0315 commit e4ab53b

File tree

5 files changed

+55
-10
lines changed

5 files changed

+55
-10
lines changed

docs/api.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ I/O & Conversion
201201
UxDataArray.to_polycollection
202202
UxDataArray.to_dataset
203203
UxDataArray.from_xarray
204+
UxDataArray.from_healpix
204205

205206

206207
UxDataset

test/test_healpix.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,10 @@ def test_from_healpix_dataset():
8080

8181
uxda = ux.UxDataset.from_healpix(xrda, face_dim="n_face")
8282
assert isinstance(uxda, ux.UxDataset)
83+
84+
85+
def test_invalid_cells():
86+
# 11 is not a valid number of global cells
87+
xrda = xr.DataArray(data=np.ones(11), dims=['cell']).to_dataset(name='cell')
88+
with pytest.raises(ValueError):
89+
uxda = ux.UxDataset.from_healpix(xrda)

uxarray/core/dataarray.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from uxarray.grid import Grid
2727
from uxarray.grid.dual import construct_dual
2828
from uxarray.grid.validation import _check_duplicate_nodes_indices
29+
from uxarray.io._healpix import get_zoom_from_cells
2930
from uxarray.plot.accessor import UxDataArrayPlotAccessor
3031
from uxarray.remap.accessor import RemapAccessor
3132
from uxarray.subset import DataArraySubsetAccessor
@@ -1345,11 +1346,12 @@ def from_healpix(
13451346
f"Please set 'face_dim' to the dimension corresponding to the healpix face dimension."
13461347
)
13471348

1348-
# Compute the HEALPix Zoom Level
1349-
zoom = np.emath.logn(4, (da.sizes[face_dim] / 12)).astype(int)
1350-
1351-
# Attach a HEALPix Grid
1352-
uxgrid = Grid.from_healpix(zoom, pixels_only=pixels_only, **kwargs)
1349+
# Attach a HEALPix Grid
1350+
uxgrid = Grid.from_healpix(
1351+
zoom=get_zoom_from_cells(da.sizes[face_dim]),
1352+
pixels_only=pixels_only,
1353+
**kwargs,
1354+
)
13531355

13541356
return cls.from_xarray(da, uxgrid, {face_dim: "n_face"})
13551357

uxarray/core/dataset.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from uxarray.grid import Grid
2020
from uxarray.grid.dual import construct_dual
2121
from uxarray.grid.validation import _check_duplicate_nodes_indices
22+
from uxarray.io._healpix import get_zoom_from_cells
2223
from uxarray.plot.accessor import UxDatasetPlotAccessor
2324
from uxarray.remap.accessor import RemapAccessor
2425

@@ -317,11 +318,12 @@ def from_healpix(
317318
f"Please set 'face_dim' to the dimension corresponding to the healpix face dimension."
318319
)
319320

320-
# Compute the HEALPix Zoom Level
321-
zoom = np.emath.logn(4, (ds.sizes[face_dim] / 12)).astype(int)
322-
323-
# Attach a HEALPix Grid
324-
uxgrid = Grid.from_healpix(zoom, pixels_only=pixels_only, **kwargs)
321+
# Attach a HEALPix Grid
322+
uxgrid = Grid.from_healpix(
323+
zoom=get_zoom_from_cells(ds.sizes[face_dim]),
324+
pixels_only=pixels_only,
325+
**kwargs,
326+
)
325327

326328
return cls.from_xarray(ds, uxgrid, {face_dim: "n_face"})
327329

uxarray/io/_healpix.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,39 @@
99
from uxarray.constants import INT_DTYPE
1010

1111

12+
def get_zoom_from_cells(cells):
13+
"""
14+
Compute the HEALPix zoom level n such that cells == 12 * 4**n.
15+
Only global HEALPix grids (i.e. exactly 12 * 4**n cells) are supported.
16+
Raises ValueError with detailed HEALPix guidance otherwise.
17+
"""
18+
if not isinstance(cells, int) or cells < 12:
19+
raise ValueError(
20+
f"Invalid cells={cells!r}: a global HEALPix grid must have "
21+
f"an integer number of cells ≥ 12 (12 base pixels at zoom=0)."
22+
)
23+
24+
if cells % 12 != 0:
25+
raise ValueError(
26+
f"Invalid cells={cells}: global HEALPix grids have exactly 12 * 4**n cells"
27+
)
28+
29+
power = cells // 12
30+
zoom = 0
31+
32+
while power > 1 and power % 4 == 0:
33+
power //= 4
34+
zoom += 1
35+
36+
if power != 1:
37+
raise ValueError(
38+
f"Invalid cells={cells}: no integer n satisfies cells==12 * 4**n. "
39+
f"Only global HEALPix grids (with cells = 12 × 4^n) are supported."
40+
)
41+
42+
return zoom
43+
44+
1245
def pix2corner_ang(
1346
nside: int, ipix: Any, nest: bool = False, lonlat: bool = False
1447
) -> Tuple[Any, Any, Any, Any]:

0 commit comments

Comments
 (0)