Skip to content

Commit

Permalink
Merge pull request #94 from cds-astro/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
fxpineau authored Mar 2, 2023
2 parents 66b0b42 + ce1ce50 commit f5852e4
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 70 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ jobs:
echo "Loop on PYBIN: $PYBIN"
"${PYBIN}/pip" install maturin
"${PYBIN}/maturin" -V
"${PYBIN}/maturin" publish -i "${PYBIN}/python" --no-sdist --skip-existing --compatibility manylinux2014 --username "$MATURIN_USERNAME" --target aarch64-unknown-linux-gnu --config "net.git-fetch-with-cli = true"
"${PYBIN}/maturin" publish -i "${PYBIN}/python" --no-sdist --skip-existing --compatibility musllinux_1_2 --username "$MATURIN_USERNAME" --target aarch64-unknown-linux-gnu --config "net.git-fetch-with-cli = true"
"${PYBIN}/maturin" publish -i "${PYBIN}/python" --skip-existing --compatibility manylinux2014 --username "$MATURIN_USERNAME" --target aarch64-unknown-linux-gnu --config "net.git-fetch-with-cli = true"
"${PYBIN}/maturin" publish -i "${PYBIN}/python" --skip-existing --compatibility musllinux_1_2 --username "$MATURIN_USERNAME" --target aarch64-unknown-linux-gnu --config "net.git-fetch-with-cli = true"
done'
build-windows-wheels:
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.12.2]

### Added

* `MOC.MAX_ORDER` and `TimeMOC.MAX_ORDER` to replace former `IntervalSet.HPX_MAX_ORDER` and `IntervalSet.TIME_MAX_ORDER`
* `MOC.to_depth29_ranges` (+test) to replace former `IntervalSet.nested`
* `TimeMOC.to_depth61_ranges`
* tests on `MOC.uniq_hpx`

### Bugfix

* :bug: return statement was missing in `MOC.uniq_hpx`


## [0.12.1]

### Added
Expand Down Expand Up @@ -53,6 +67,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* ⚠️ BREAKING: `times_to_microseconds` and `microseconds_to_times` moved from `utils` to `tmoc`.
* ⚠️ BREAKING: `uniq` removed from `IntervalSet`, but replacing method `uniq_hpx` added to `MOC`
+ ⚠️ BREAKING: the output of `uniq_hpx` is not sorted (but follow the order of the cells in the internal range list)
* ⚠️ BREAKING: `STMOC.query_by_time` now takes in input a `TimeMOC`
* ⚠️ BREAKING: `STMOC.query_by_space` now returns a `MOC`
* ⚠️ BREAKING: `TimeMOC.contains` does not take any longer a time resolution as input parameter
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "MOCPy"
version = "0.12.1"
version = "0.12.2"
authors = [
"Matthieu Baumann <[email protected]>",
"Thomas Boch <[email protected]>",
Expand Down
4 changes: 2 additions & 2 deletions codemeta.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"dateModified": "2022-02-04",
"issueTracker": "https://github.com/cds-astro/mocpy/issues",
"name": "MOCpy",
"version": "0.12.1",
"softwareVersion": "0.12.1",
"version": "0.12.2",
"softwareVersion": "0.12.2",
"description": "Python library to easily create and manipulate MOCs (Multi-Order Coverage maps)",
"applicationCategory": ["Astronomy", "Science"],
"funding": "ESCAPE 824064, ASTERICS 653477",
Expand Down
27 changes: 23 additions & 4 deletions python/mocpy/moc/moc.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ class MOC(AbstractMOC):
5. Serialize `~mocpy.moc.MOC` objects to `astropy.io.fits.HDUList` or JSON dictionary and save it to a file.
"""

# Maximum order (or depth) of a MOC
# (do not remove since it may be used externally).
MAX_ORDER = np.uint8(29)

__create_key = object()

def __init__(self, create_key, store_index):
Expand Down Expand Up @@ -1053,13 +1057,15 @@ def from_healpix_cells(cls, ipix, depth, max_depth):
@classmethod
def from_depth29_ranges(cls, max_depth, ranges):
"""
Create a MOC from a set of HEALPix ranges at order 29.
Create a MOC from a set of ranges of HEALPix Nested indices at order 29.
For each range, the lower bound is inclusive and the upper bound is exclusive.
The range `[0, 12*4^29[` represents the full-sky.
Parameters
----------
max_depth : int, The resolution of the MOC
ranges : `~numpy.ndarray`, optional
a N x 2 numpy array representing the set of depth 29 ranges.
a N x 2 numpy array representing the set of depth 29 HEALPix nested ranges.
defaults to `np.zeros((0, 2), dtype=np.uint64)`
Returns
Expand Down Expand Up @@ -1432,8 +1438,21 @@ def from_string(cls, value, format="ascii"): # noqa: A002

@property
def uniq_hpx(self):
"""Return a `np.array` of the uniq HEALPIx indices of the cell in the MOC."""
mocpy.to_uniq_hpx(self._store_index)
"""
Return a `np.array` of the uniq HEALPIx indices of the cell in the MOC.
Warning
-------
The output is not sorted, the order follow the order of HEALPix cells in
the underlying sorted array of depth29 nested ranges, i.e. the natural order
of the cells is the underlying z-order curve.
"""
return mocpy.to_uniq_hpx(self._store_index)

@property
def to_depth29_ranges(self):
"""Return the list of order 29 HEALPix nested ranges this MOC contains."""
return mocpy.to_ranges(self._store_index)

def to_rgba(self, y_size=300):
"""
Expand Down
127 changes: 91 additions & 36 deletions python/mocpy/tests/test_moc.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,44 +51,84 @@ def isets():
dtype=np.uint64,
),
)
return dict(a=a, b=b)
return {"a": a, "b": b}


def test_interval_set_consistency(isets):
assert isets["a"] == MOC.from_depth29_ranges(
29, np.array([[27, 126]], dtype=np.uint64)
29,
np.array([[27, 126]], dtype=np.uint64),
)
assert isets["b"] == MOC.from_depth29_ranges(
29, np.array([[9, 61], [68, 105]], dtype=np.uint64)
29,
np.array([[9, 61], [68, 105]], dtype=np.uint64),
)


def test_uniq_hpx():
moc = MOC.from_depth29_ranges(29, np.array([[0, 1]], dtype=np.uint64))
uniq_hpx = np.array([4 * 4**29], dtype=np.uint64)
assert moc.uniq_hpx == uniq_hpx

moc = MOC.from_depth29_ranges(29, np.array([[7, 76]], dtype=np.uint64))
uniq_hpx = np.array(
[
1 + 4 * 4**27,
2 + 4 * 4**27,
3 + 4 * 4**27,
2 + 4 * 4**28,
3 + 4 * 4**28,
16 + 4 * 4**28,
17 + 4 * 4**28,
18 + 4 * 4**28,
7 + 4 * 4**29,
],
dtype=np.uint64,
)
assert (np.sort(moc.uniq_hpx) == uniq_hpx).all()


def test_to_depth29_ranges(isets):
l = isets["a"].to_depth29_ranges
r = np.array([[27, 126]], dtype=np.uint64)
assert np.array_equal(l, r)
l = isets["b"].to_depth29_ranges
r = np.array([[9, 61], [68, 105]], dtype=np.uint64)
assert np.array_equal(l, r)


def test_interval_set_union(isets):
assert isets["a"].union(isets["b"]) == MOC.from_depth29_ranges(
29, np.array([[9, 126]], dtype=np.uint64)
29,
np.array([[9, 126]], dtype=np.uint64),
)
assert isets["a"].union(MOC.new_empty(29)) == MOC.from_depth29_ranges(
29, np.array([[27, 126]], dtype=np.uint64)
29,
np.array([[27, 126]], dtype=np.uint64),
)
assert MOC.new_empty(29).union(isets["a"]) == MOC.from_depth29_ranges(
29, np.array([[27, 126]], dtype=np.uint64)
29,
np.array([[27, 126]], dtype=np.uint64),
)


def test_interval_set_intersection(isets):
assert isets["a"].intersection(isets["b"]) == MOC.from_depth29_ranges(
29, np.array([[27, 61], [68, 105]], dtype=np.uint64)
29,
np.array([[27, 61], [68, 105]], dtype=np.uint64),
)
assert isets["a"].intersection(MOC.new_empty(29)) == MOC.new_empty(29)
assert MOC.new_empty(29).intersection(isets["a"]) == MOC.new_empty(29)


def test_interval_set_difference(isets):
assert isets["a"].difference(isets["b"]) == MOC.from_depth29_ranges(
29, np.array([[61, 68], [105, 126]], dtype=np.uint64)
29,
np.array([[61, 68], [105, 126]], dtype=np.uint64),
)
assert isets["b"].difference(isets["a"]) == MOC.from_depth29_ranges(
29, np.array([[9, 27]], dtype=np.uint64)
29,
np.array([[9, 27]], dtype=np.uint64),
)
assert MOC.new_empty(29).difference(isets["a"]) == MOC.new_empty(29)
assert isets["a"].difference(MOC.new_empty(29)) == isets["a"]
Expand Down Expand Up @@ -125,17 +165,21 @@ def test_interval_min_depth():

def test_complement():
assert MOC.from_depth29_ranges(
max_depth=29, ranges=None
max_depth=29,
ranges=None,
).complement() == MOC.from_depth29_ranges(
max_depth=29, ranges=np.array([[0, 12 * 4**29]], dtype=np.uint64)
max_depth=29,
ranges=np.array([[0, 12 * 4**29]], dtype=np.uint64),
)
assert MOC.new_empty(
max_depth=29
max_depth=29,
).complement().complement() == MOC.from_depth29_ranges(max_depth=29, ranges=None)
assert MOC.from_depth29_ranges(
29, np.array([[1, 2], [6, 8], [5, 6]], dtype=np.uint64)
29,
np.array([[1, 2], [6, 8], [5, 6]], dtype=np.uint64),
).complement() == MOC.from_depth29_ranges(
29, np.array([[0, 1], [2, 5], [8, 12 * 4**29]], dtype=np.uint64)
29,
np.array([[0, 1], [2, 5], [8, 12 * 4**29]], dtype=np.uint64),
)


Expand Down Expand Up @@ -193,7 +237,7 @@ def test_moc_from_lonlat(lonlat_gen_f, size):
def test_from_healpix_cells():
ipix = np.array([40, 87, 65])
depth = np.array([3, 3, 3])
fully_covered = np.array([True, True, True])
np.array([True, True, True])

MOC.from_healpix_cells(max_depth=29, ipix=ipix, depth=depth)

Expand All @@ -213,7 +257,9 @@ def test_moc_consistent_with_aladin():
table = parse_single_table("resources/I_125A_catalog.vot").to_table()

moc = MOC.from_lonlat(
table["_RAJ2000"].T * u.deg, table["_DEJ2000"].T * u.deg, max_norder=8
table["_RAJ2000"].T * u.deg,
table["_DEJ2000"].T * u.deg,
max_norder=8,
)

assert moc == truth
Expand Down Expand Up @@ -264,7 +310,7 @@ def test_moc_serialize_and_from_json(moc_from_json):
"6": [4500],
"7": [],
"8": [45],
}
},
),
"5/8-10 42-46 54\n\r 6/4500 8/45",
),
Expand All @@ -290,7 +336,7 @@ def test_from_str(expected, moc_str):
"6": [4500],
"7": [],
"8": [45],
}
},
),
"5/8-10 42-46 54\n\r 6/4500 8/45",
),
Expand Down Expand Up @@ -342,7 +388,7 @@ def test_moc_serialize_to_json(moc_from_fits_image):
"6": [4500],
"7": [],
"8": [45],
}
},
),
"5/8-10 42-46 54 6/4500 8/45 ",
),
Expand Down Expand Up @@ -435,7 +481,9 @@ def test_moc_contains(order):
should_be_outside_arr = moc.contains_lonlat(lon=lon, lat=lat, keep_inside=False)
assert not should_be_outside_arr.any()
should_be_inside_arr = moc_complement.contains_lonlat(
lon=lon, lat=lat, keep_inside=False
lon=lon,
lat=lat,
keep_inside=False,
)
assert should_be_inside_arr.all()

Expand Down Expand Up @@ -503,12 +551,12 @@ def mocs():
moc2 = {"1": [30]}
moc2_increased = {"0": [7], "1": [8, 9, 25, 43, 41]}

return dict(
moc1=MOC.from_json(moc1),
moc1_increased=MOC.from_json(moc1_increased),
moc2=MOC.from_json(moc2),
moc2_increased=MOC.from_json(moc2_increased),
)
return {
"moc1": MOC.from_json(moc1),
"moc1_increased": MOC.from_json(moc1_increased),
"moc2": MOC.from_json(moc2),
"moc2_increased": MOC.from_json(moc2_increased),
}


def test_add_neighbours(mocs):
Expand Down Expand Up @@ -542,31 +590,31 @@ def test_neighbours(mocs):
def mocs_op():
moc1 = MOC.from_json({"0": [0, 2, 3, 4, 5]})
moc2 = MOC.from_json({"0": [0, 1, 7, 4, 3]})
return dict(first=moc1, second=moc2)
return {"first": moc1, "second": moc2}


def test_moc_union(mocs_op):
assert mocs_op["first"].union(mocs_op["second"]) == MOC.from_json(
{"0": [0, 1, 2, 3, 4, 5, 7]}
{"0": [0, 1, 2, 3, 4, 5, 7]},
)
assert mocs_op["first"] + mocs_op["second"] == MOC.from_json(
{"0": [0, 1, 2, 3, 4, 5, 7]}
{"0": [0, 1, 2, 3, 4, 5, 7]},
)
assert mocs_op["first"] | mocs_op["second"] == MOC.from_json(
{"0": [0, 1, 2, 3, 4, 5, 7]}
{"0": [0, 1, 2, 3, 4, 5, 7]},
)


def test_moc_intersection(mocs_op):
assert mocs_op["first"].intersection(mocs_op["second"]) == MOC.from_json(
{"0": [0, 3, 4]}
{"0": [0, 3, 4]},
)
assert mocs_op["first"] & mocs_op["second"] == MOC.from_json({"0": [0, 3, 4]})


def test_moc_difference(mocs_op):
assert mocs_op["first"].difference(mocs_op["second"]) == MOC.from_json(
{"0": [2, 5]}
{"0": [2, 5]},
)
assert mocs_op["first"] - mocs_op["second"] == MOC.from_json({"0": [2, 5]})

Expand All @@ -587,7 +635,7 @@ def test_from_fits_old():
(
MOC.from_json({"0": [1, 3]}),
MOC.from_json({"0": [0, 2, 4, 5, 6, 7, 8, 9, 10, 11]}),
)
),
],
)
def test_moc_complement(input, expected):
Expand Down Expand Up @@ -616,7 +664,8 @@ def test_from_valued_healpix_cells_different_sizes():
values = np.array([])

with pytest.raises(
ValueError, match="`uniq` and values do not have the same size."
ValueError,
match="`uniq` and values do not have the same size.",
):
MOC.from_valued_healpix_cells(uniq, values, 12)

Expand All @@ -638,7 +687,11 @@ def test_from_valued_healpix_cells_weird_values(cumul_from, cumul_to):
values = np.array([-1.0])

MOC.from_valued_healpix_cells(
uniq, values, 12, cumul_from=cumul_from, cumul_to=cumul_to
uniq,
values,
12,
cumul_from=cumul_from,
cumul_to=cumul_to,
)


Expand Down Expand Up @@ -673,7 +726,9 @@ def test_from_valued_healpix_cells_bayestar():
def test_from_valued_healpix_cells_bayestar_and_split():
fits_mom_filename = "./resources/bayestar.multiorder.fits"
moc = MOC.from_multiordermap_fits_file(
fits_mom_filename, cumul_from=0.0, cumul_to=0.9
fits_mom_filename,
cumul_from=0.0,
cumul_to=0.9,
)
count = moc.split_count()
assert count == 2
Expand Down
Loading

0 comments on commit f5852e4

Please sign in to comment.