diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 0d982961..239bedf8 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -26,7 +26,7 @@ jobs: MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN_FXP }} run: | source $HOME/.cargo/env - for PYBIN in /opt/python/cp3{8,9,10,11,12}-*/bin; do + for PYBIN in /opt/python/cp3{8,9,10,11,12,13}-*/bin; do "${PYBIN}/pip" install --upgrade pip "${PYBIN}/pip" install maturin "${PYBIN}/maturin" publish -i "${PYBIN}/python" --skip-existing --compatibility manylinux_2_28 @@ -52,7 +52,7 @@ jobs: MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN_FXP }} run: | source $HOME/.cargo/env - for PYBIN in /opt/python/cp3{8,9,10,11,12}-*/bin; do + for PYBIN in /opt/python/cp3{8,9,10,11,12,13}-*/bin; do "${PYBIN}/pip" install --upgrade pip "${PYBIN}/pip" install maturin "${PYBIN}/maturin" publish -i "${PYBIN}/python" --skip-existing --compatibility manylinux2014 @@ -64,7 +64,7 @@ jobs: runs-on: ubuntu-latest # CentOS 7 32 bits Docker Hub image that 'build-linux-wheels' executes in. # See https://github.com/pypa/manylinux for this particular container: - # * CPython 3.7, 3.8, 3.9, 3.10, 3.11, and 3.12 installed in /opt/python/- + # * CPython 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 13 installed in /opt/python/- env: MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN_FXP }} img: quay.io/pypa/manylinux2014_i686 @@ -81,7 +81,7 @@ jobs: ${{ env.img }} \ bash -exc 'curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \ source $HOME/.cargo/env && \ - for PYBIN in /opt/python/cp3{8,9,10,11,12}-*/bin; do + for PYBIN in /opt/python/cp3{8,9,10,11,12,13}-*/bin; do echo "Loop on PYBIN: $PYBIN" "${PYBIN}/pip" install --upgrade pip "${PYBIN}/pip" install maturin @@ -108,7 +108,7 @@ jobs: ${{ env.img }} \ bash -exc 'curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-host aarch64-unknown-linux-gnu -y && \ source $HOME/.cargo/env && \ - for PYBIN in /opt/python/cp3{8,9,10,11,12}-*/bin; do + for PYBIN in /opt/python/cp3{8,9,10,11,12,13}-*/bin; do echo "Loop on PYBIN: $PYBIN" "${PYBIN}/pip" install maturin "${PYBIN}/maturin" -V @@ -158,7 +158,7 @@ jobs: fail-fast: false matrix: os: [macOS-latest] - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] steps: # Checkout the project - uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 374cb12b..6cd55821 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [0.17.0] + ### Added * Add support of `regions.Regions` [#163] diff --git a/Cargo.toml b/Cargo.toml index ea618825..f75bd21d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "MOCPy" -version = "0.16.2" +version = "0.17.0" authors = [ "Matthieu Baumann ", "Thomas Boch ", @@ -28,24 +28,24 @@ bench = true crate-type = ["cdylib"] [dependencies] -# moc = { version = "0.15", features = ["storage"] } -moc = { git = 'https://github.com/cds-astro/cds-moc-rust', rev = '361eb278fe782bfc053433495c33e3f16e20cdbd', features = ["storage"] } -healpix = { package = "cdshealpix", version = "0.6" } +moc = { version = "0.17", features = ["storage"] } +#moc = { git = 'https://github.com/cds-astro/cds-moc-rust', rev = '361eb278fe782bfc053433495c33e3f16e20cdbd', features = ["storage"] } +healpix = { package = "cdshealpix", version = "0.7" } # healpix = { package = "cdshealpix", git = 'https://github.com/cds-astro/cds-healpix-rust', branch = 'master' } rayon = "1.10" num_threads = "0.1" [dependencies.numpy] -version = "0.21" +version = "0.22" [dependencies.ndarray] -version = "0.15" +version = "0.16" default-features = false # do not include the default features, and optionally # cherry-pick individual features features = ["rayon"] [dependencies.pyo3] -version = "0.21" +version = "0.22" features = ["extension-module"] [profile.release] diff --git a/codemeta.json b/codemeta.json index a76d7ccb..6532c3e0 100644 --- a/codemeta.json +++ b/codemeta.json @@ -9,8 +9,8 @@ "dateModified": "2023-12-04", "issueTracker": "https://github.com/cds-astro/mocpy/issues", "name": "MOCpy", - "version": "0.16.2", - "softwareVersion": "0.16.2", + "version": "0.17.0", + "softwareVersion": "0.17.0", "description": "Python library to easily create and manipulate MOCs (Multi-Order Coverage maps)", "applicationCategory": ["Astronomy", "Science"], "funding": "ESCAPE 824064, ASTERICS 653477", diff --git a/pyproject.toml b/pyproject.toml index aa47b0a2..5a4e7cbe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ requires-python = ">=3.8" dependencies = [ "astropy<5.3; python_version == '3.8'", "astropy; python_version > '3.8'", - "numpy<2.0", + "numpy", "matplotlib", # Used in fill and border "cdshealpix>=0.6.4", # Used in fill and border "networkx>=2.5", # Used in get_boundaries @@ -18,8 +18,16 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Rust", "License :: OSI Approved :: BSD License", - "Topic :: Scientific/Engineering :: Astronomy" + "Topic :: Scientific/Engineering :: Astronomy", + "Intended Audience :: Science/Research", + "Intended Audience :: Education", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ] +keywords=["astronomy", "astrophysics", "HEALPix"] [project.optional-dependencies] # optional to load FITS from URLs @@ -29,18 +37,17 @@ astropy_regions = ["regions"] # for the documentation docs = [ "astropy-sphinx-theme", - "astropy_healpix", "nbsphinx", "numpydoc", "sphinx-astropy", "sphinx-collections", "sphinx-copybutton", "sphinx-gallery", - "sphinxcontrib-bibtex" + "sphinxcontrib-bibtex", + "ipython" # for syntaxic coloration in docs ] # for developpement dev = [ - "astropy_healpix", "pre-commit >= 2.20", "pytest > 6.0", "pytest-mock", @@ -58,7 +65,10 @@ notebooks = [ ] [project.urls] -repository = "https://github.com/cds-astro/mocpy" +Documentation = "https://cds-astro.github.io/mocpy/" +Repository = "https://github.com/cds-astro/mocpy" +Issues = "https://github.com/cds-astro/mocpy/issues" +Changelog = "https://github.com/cds-astro/mocpy/blob/master/CHANGELOG.md" # Build a mocpy-x.x.x.tar.gz containing sources (from maturin). [build-system] diff --git a/python/mocpy/fmoc/fmoc.py b/python/mocpy/fmoc/fmoc.py index 2abcdd48..5fa23202 100644 --- a/python/mocpy/fmoc/fmoc.py +++ b/python/mocpy/fmoc/fmoc.py @@ -44,7 +44,7 @@ def max_order(self): -------- >>> from mocpy import FrequencyMOC >>> fmoc = FrequencyMOC.from_json({8: [12, 14, 16], 22: [120, 121, 122]}) - >>> fmoc.max_order + >>> print(fmoc.max_order) 22 """ depth = mocpy.get_fmoc_depth(self.store_index) diff --git a/python/mocpy/moc/moc.py b/python/mocpy/moc/moc.py index c7178695..b9edfdb3 100644 --- a/python/mocpy/moc/moc.py +++ b/python/mocpy/moc/moc.py @@ -2346,7 +2346,7 @@ def wcs( >>> import matplotlib.pyplot as plt >>> moc = MOC.from_str("2/2-25 28 29 4/0 6/") >>> fig = plt.figure() - >>> moc.wcs(fig) # DOCTEST: +IGNORE_RESULT + >>> moc.wcs(fig) # doctest: +SKIP WCS Keywords Number of WCS axes: 2 diff --git a/python/mocpy/version.py b/python/mocpy/version.py index 7f33bc09..fd86b3ee 100644 --- a/python/mocpy/version.py +++ b/python/mocpy/version.py @@ -1 +1 @@ -__version__ = "0.16.2" +__version__ = "0.17.0" diff --git a/src/lib.rs b/src/lib.rs index 599f972c..3c0a4551 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,13 @@ use std::{f64::consts::FRAC_PI_3, ops::Range}; -use ndarray::Array; #[cfg(not(target_arch = "wasm32"))] use num_threads::num_threads; use numpy::{ IntoPyArray, Ix2, Ix3, PyArray1, PyArray2, PyArray3, PyArrayDyn, PyArrayMethods, PyReadonlyArray1, PyReadonlyArray2, PyReadonlyArrayDyn, PyUntypedArrayMethods, }; +use numpy::ndarray::Array; + use pyo3::{ exceptions::{PyIOError, PyValueError}, prelude::{pymodule, Bound, PyModule, PyResult, Python}, @@ -172,6 +173,7 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { /// * `delta_depth`: precision parameter /// * `n_threads`: number of threads to use (max number of threads if `n_threads=None`. #[pyfn(m)] + #[pyo3(signature = (lon_deg, lat_deg, radius_deg, depth, delta_depth, n_threads=None))] fn from_same_cones( lon_deg: PyReadonlyArrayDyn, lat_deg: PyReadonlyArrayDyn, @@ -248,6 +250,7 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { /// * `delta_depth`: precision parameter /// * `n_threads`: number of threads to use (max number of threads if `n_threads=None`. #[pyfn(m)] + #[pyo3(signature = (lon_deg, lat_deg, radius_deg, depth, delta_depth, n_threads=None))] fn from_cones( lon_deg: PyReadonlyArrayDyn, lat_deg: PyReadonlyArrayDyn, @@ -328,6 +331,7 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { /// * `delta_depth`: precision parameter /// * `n_threads`: number of threads to use (max number of threads if `n_threads=None`. #[pyfn(m)] + #[pyo3(signature = (lon_deg, lat_deg, radius_deg, depth, delta_depth, n_threads=None))] fn from_small_cones( lon_deg: PyReadonlyArrayDyn, lat_deg: PyReadonlyArrayDyn, @@ -403,6 +407,7 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { /// * `delta_depth`: precision parameter /// * `n_threads`: number of threads to use (max number of threads if `n_threads=None`. #[pyfn(m)] + #[pyo3(signature = (lon_deg, lat_deg, radius_deg, depth, delta_depth, n_threads=None))] fn from_large_cones( lon_deg: PyReadonlyArrayDyn, lat_deg: PyReadonlyArrayDyn, @@ -507,6 +512,7 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { /// # Output /// - The MOC indices in the storage #[pyfn(m)] + #[pyo3(signature = (lon_deg, lat_deg, a, b, angle, depth, n_threads=None))] pub fn from_same_boxes( lon_deg: PyReadonlyArrayDyn, lat_deg: PyReadonlyArrayDyn, @@ -591,6 +597,7 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { /// # Output /// - The MOC indices in the storage #[pyfn(m)] + #[pyo3(signature = (lon_deg, lat_deg, a, b, pa, depth, n_threads=None))] pub fn from_boxes( lon_deg: PyReadonlyArrayDyn, lat_deg: PyReadonlyArrayDyn, @@ -685,6 +692,7 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { /// # Output /// - The MOC index in the storage #[pyfn(m)] + #[pyo3(signature = (lon_deg, lat_deg, a, b, pa, depth, n_threads=None))] pub fn from_small_boxes( lon_deg: PyReadonlyArrayDyn, lat_deg: PyReadonlyArrayDyn, @@ -773,6 +781,7 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { /// # Output /// - The MOC index in the storage #[pyfn(m)] + #[pyo3(signature = (lon_deg, lat_deg, a, b, pa, depth, n_threads=None))] pub fn from_large_boxes( lon_deg: PyReadonlyArrayDyn, lat_deg: PyReadonlyArrayDyn, @@ -970,6 +979,7 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { /// * `depth`: MOC depth /// * `n_threads`: number of threads to use (max number of threads if `n_threads=None`. #[pyfn(m)] + #[pyo3(signature = (lon_lat_deg, complement, depth, n_threads=None))] pub fn from_polygons( lon_lat_deg: Vec>, complement: bool, @@ -1129,9 +1139,9 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { lat: PyReadonlyArrayDyn, d2: u8, ) -> PyResult { - let times = times.as_array().to_owned().into_raw_vec(); - let lon = lon.as_array().to_owned().into_raw_vec(); - let lat = lat.as_array().to_owned().into_raw_vec(); + let times = times.to_vec().map_err(PyValueError::new_err)?; + let lon = lon.to_vec().map_err(PyValueError::new_err)?; + let lat = lat.to_vec().map_err(PyValueError::new_err)?; U64MocStore::get_global_store() .create_from_times_positions_approx(times, lon, lat, d1, d2) @@ -1167,9 +1177,9 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { lat: PyReadonlyArrayDyn, d2: u8, ) -> PyResult { - let times = times.as_array().to_owned().into_raw_vec(); - let lon = lon.as_array().to_owned().into_raw_vec(); - let lat = lat.as_array().to_owned().into_raw_vec(); + let times = times.to_vec().map_err(PyValueError::new_err)?; + let lon = lon.to_vec().map_err(PyValueError::new_err)?; + let lat = lat.to_vec().map_err(PyValueError::new_err)?; U64MocStore::get_global_store() .create_from_times_positions(times, lon, lat, d1, d2) @@ -1214,10 +1224,10 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { lat: PyReadonlyArrayDyn, d2: u8, ) -> PyResult { - let times_min = times_min.as_array().to_owned().into_raw_vec(); - let times_max = times_max.as_array().to_owned().into_raw_vec(); - let lon = lon.as_array().to_owned().into_raw_vec(); - let lat = lat.as_array().to_owned().into_raw_vec(); + let times_min = times_min.to_vec().map_err(PyValueError::new_err)?; + let times_max = times_max.to_vec().map_err(PyValueError::new_err)?; + let lon = lon.to_vec().map_err(PyValueError::new_err)?; + let lat = lat.to_vec().map_err(PyValueError::new_err)?; U64MocStore::get_global_store() .create_from_time_ranges_positions_approx(times_min, times_max, d1, lon, lat, d2) @@ -1256,10 +1266,10 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { lat: PyReadonlyArrayDyn, d2: u8, ) -> PyResult { - let times_min = times_min.as_array().to_owned().into_raw_vec(); - let times_max = times_max.as_array().to_owned().into_raw_vec(); - let lon = lon.as_array().to_owned().into_raw_vec(); - let lat = lat.as_array().to_owned().into_raw_vec(); + let times_min = times_min.to_vec().map_err(PyValueError::new_err)?; + let times_max = times_max.to_vec().map_err(PyValueError::new_err)?; + let lon = lon.to_vec().map_err(PyValueError::new_err)?; + let lat = lat.to_vec().map_err(PyValueError::new_err)?; U64MocStore::get_global_store() .create_from_time_ranges_positions(times_min, times_max, d1, lon, lat, d2) @@ -1303,14 +1313,14 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { d1: u8, spatial_coverages: PyReadonlyArrayDyn, ) -> PyResult { - let times_min = times_min.as_array().to_owned().into_raw_vec(); - let times_max = times_max.as_array().to_owned().into_raw_vec(); + let times_min = times_min.to_vec().map_err(PyValueError::new_err)?; + let times_max = times_max.to_vec().map_err(PyValueError::new_err)?; if times_min.len() != times_max.len() { return Err(PyValueError::new_err( "`times_min` and `times_max` do not have the same size.", )); } - let spatial_coverage_indices = spatial_coverages.as_array().to_owned().into_raw_vec(); + let spatial_coverage_indices = spatial_coverages.to_vec().map_err(PyValueError::new_err)?; if times_min.len() != spatial_coverage_indices.len() { return Err(PyValueError::new_err( "`times` and `spatial indices` do not have the same size.", @@ -1357,14 +1367,14 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { d1: u8, spatial_coverages: PyReadonlyArrayDyn, ) -> PyResult { - let times_min = times_min.as_array().to_owned().into_raw_vec(); - let times_max = times_max.as_array().to_owned().into_raw_vec(); + let times_min = times_min.to_vec().map_err(PyValueError::new_err)?; + let times_max = times_max.to_vec().map_err(PyValueError::new_err)?; if times_min.len() != times_max.len() { return Err(PyValueError::new_err( "`times_min` and `times_max` do not have the same size.", )); } - let spatial_coverage_indices = spatial_coverages.as_array().to_owned().into_raw_vec(); + let spatial_coverage_indices = spatial_coverages.to_vec().map_err(PyValueError::new_err)?; if times_min.len() != spatial_coverage_indices.len() { return Err(PyValueError::new_err( "`times` and `spatial indices` do not have the same size.", @@ -2304,6 +2314,7 @@ fn mocpy(m: &Bound<'_, PyModule>) -> PyResult<()> { /// Same as `multiorder_probdens_map_sum_in_smoc` but applied on multiple S-MOCs. #[pyfn(m)] + #[pyo3(signature = (indices, uniq, uniq_mask, probdens, probdens_mask, n_threads=None))] fn multi_multiorder_probdens_map_sum_in_smoc<'a>( py: Python<'a>, indices: PyReadonlyArrayDyn<'a, usize>,