diff --git a/.github/workflows/test_and_build.yaml b/.github/workflows/test_and_build.yaml index 4dd91349..da3ffe85 100644 --- a/.github/workflows/test_and_build.yaml +++ b/.github/workflows/test_and_build.yaml @@ -21,7 +21,7 @@ jobs: fail-fast: false matrix: os: ["ubuntu-latest", "macos-latest"] - python-version: ["3.11"] + python-version: ["3.11", "3.12"] steps: - uses: actions/checkout@v4 - uses: conda-incubator/setup-miniconda@v3 @@ -29,7 +29,7 @@ jobs: auto-update-conda: true python-version: ${{ matrix.python-version }} miniforge-variant: Miniforge3 - channels: conda-forge,defaults + channels: conda-forge use-mamba: true channel-priority: strict show-channel-urls: true diff --git a/optional-requirements.txt b/optional-requirements.txt new file mode 100644 index 00000000..0e0e55de --- /dev/null +++ b/optional-requirements.txt @@ -0,0 +1,2 @@ +openorb +openorb-data-de405 diff --git a/pyproject.toml b/pyproject.toml index cc0ce860..aad5ebc8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ classifiers = [ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Scientific/Engineering :: Astronomy", ] urls = {documentation = "https://rubin-sim.lsst.io", repository = "https://github.com/lsst/rubin_sim" } @@ -38,7 +39,6 @@ dependencies = [ "shapely", "skyfield", "skyproj", - "pyoorb", "tqdm", "rubin-scheduler", "pyarrow", diff --git a/requirements.txt b/requirements.txt index 188bef80..6eec5649 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,8 +11,6 @@ sqlalchemy astropy pytables h5py -openorb -openorb-data-de405 astroplan colorcet cycler diff --git a/rubin_sim/maf/maf_contrib/periodic_star_modulation_metric.py b/rubin_sim/maf/maf_contrib/periodic_star_modulation_metric.py index af92e052..3b053960 100644 --- a/rubin_sim/maf/maf_contrib/periodic_star_modulation_metric.py +++ b/rubin_sim/maf/maf_contrib/periodic_star_modulation_metric.py @@ -116,7 +116,7 @@ def __init__( self.amplitude = amplitude self.time_interval = time_interval if random_phase: - self.phase = np.NaN + self.phase = np.nan else: self.phase = phase self.n_monte = n_monte diff --git a/rubin_sim/maf/metrics/base_metric.py b/rubin_sim/maf/metrics/base_metric.py index 030c07ab..3f21f8c3 100644 --- a/rubin_sim/maf/metrics/base_metric.py +++ b/rubin_sim/maf/metrics/base_metric.py @@ -140,7 +140,7 @@ def __init__( ): # Turn cols into numpy array so we know # we can iterate over the columns. - self.col_name_arr = np.array(col, copy=False, ndmin=1) + self.col_name_arr = np.array(col, copy=None, ndmin=1) # To support simple metrics operating on a single column, # set self.colname if len(self.col_name_arr) == 1: diff --git a/rubin_sim/maf/metrics/sn_cadence_metric.py b/rubin_sim/maf/metrics/sn_cadence_metric.py index 201ae2cd..4c9a886d 100644 --- a/rubin_sim/maf/metrics/sn_cadence_metric.py +++ b/rubin_sim/maf/metrics/sn_cadence_metric.py @@ -73,7 +73,7 @@ def __init__( def run(self, data_slice, slice_point=None): # Cut down to only include filters in correct wave range. - good_filters = np.in1d(data_slice["filter"], self.filter_names) + good_filters = np.isin(data_slice["filter"], self.filter_names) data_slice = data_slice[good_filters] if data_slice.size == 0: return None diff --git a/rubin_sim/maf/metrics/sn_n_sn_metric.py b/rubin_sim/maf/metrics/sn_n_sn_metric.py index 264c7095..97bd064b 100644 --- a/rubin_sim/maf/metrics/sn_n_sn_metric.py +++ b/rubin_sim/maf/metrics/sn_n_sn_metric.py @@ -254,7 +254,7 @@ def run(self, data_slice, slice_point): data_slice[self.m5_col] = new_m5 # select observations filter - good_filters = np.in1d(data_slice[self.filter_col], list(self.bands)) + good_filters = np.isin(data_slice[self.filter_col], list(self.bands)) data_slice = data_slice[good_filters] # coaddition per night and per band (if requested by the user) diff --git a/rubin_sim/maf/metrics/sn_snr_metric.py b/rubin_sim/maf/metrics/sn_snr_metric.py index aa656910..16f1b81f 100644 --- a/rubin_sim/maf/metrics/sn_snr_metric.py +++ b/rubin_sim/maf/metrics/sn_snr_metric.py @@ -107,7 +107,7 @@ def run(self, data_slice, slice_point=None): ------- detection rate : `float` """ - good_filters = np.in1d(data_slice["filter"], self.filter_names) + good_filters = np.isin(data_slice["filter"], self.filter_names) data_slice = data_slice[good_filters] if data_slice.size == 0: return None @@ -134,7 +134,7 @@ def run(self, data_slice, slice_point=None): if self.info_season is None: return 0.0 - sel = data_slice[np.in1d(data_slice["season"], np.array(seasons))] + sel = data_slice[np.isin(data_slice["season"], np.array(seasons))] detect_frac = None if len(sel) >= 5: diff --git a/rubin_sim/maf/metrics/weak_lensing_systematics_metric.py b/rubin_sim/maf/metrics/weak_lensing_systematics_metric.py index d5b13f16..7fc9919b 100644 --- a/rubin_sim/maf/metrics/weak_lensing_systematics_metric.py +++ b/rubin_sim/maf/metrics/weak_lensing_systematics_metric.py @@ -202,7 +202,7 @@ def run(self, data_slice, slice_point): # find all entries where exposure time is long enough and # in the detection bands exptime_msk = data_slice[self.exp_time_col] > self.min_exp_time - filter_msk = np.in1d(data_slice[self.filter_col], self.det_bands) + filter_msk = np.isin(data_slice[self.filter_col], self.det_bands) tot_msk = exptime_msk & filter_msk res = np.sum(data_slice[self.exp_time_col][tot_msk]) diff --git a/rubin_sim/maf/slicers/healpix_subset_slicer.py b/rubin_sim/maf/slicers/healpix_subset_slicer.py index 2eed222f..791d5500 100644 --- a/rubin_sim/maf/slicers/healpix_subset_slicer.py +++ b/rubin_sim/maf/slicers/healpix_subset_slicer.py @@ -74,7 +74,7 @@ def make_wfd_subset_slicer(nside=64, use_cache=True, wfd_labels=None): if wfd_labels is None: wfd_labels = ["lowdust", "euclid_overlap", "virgo", "bulgy", "LMC_SMC"] footprints, labels = get_current_footprint(nside=nside) - wfdpix = np.where(np.in1d(labels, wfd_labels))[0] + wfdpix = np.where(np.isin(labels, wfd_labels))[0] slicer = HealpixSubsetSlicer(nside=nside, hpid=wfdpix, use_cache=use_cache) return slicer diff --git a/rubin_sim/maf/slicers/time_interval_slicers.py b/rubin_sim/maf/slicers/time_interval_slicers.py index 1b925f0e..9a5b0fe5 100644 --- a/rubin_sim/maf/slicers/time_interval_slicers.py +++ b/rubin_sim/maf/slicers/time_interval_slicers.py @@ -52,7 +52,7 @@ def __init__( self, interval_seconds=90, mjd_column_name="observationStartMJD", - badval=np.NaN, + badval=np.nan, verbose=False, ): super().__init__(verbose=verbose, badval=badval) @@ -148,7 +148,7 @@ def __init__( mjd_column_name="observationStartMJD", duration_column_name="visitTime", note_column_name="scheduler_note", - badval=np.NaN, + badval=np.nan, verbose=False, ): super().__init__(verbose=verbose, badval=badval) @@ -242,7 +242,7 @@ def __init__( mjd_column_name="observationStartMJD", duration_column_name="visitTime", extra_column_names=tuple(), - badval=np.NaN, + badval=np.nan, verbose=False, ): super().__init__(verbose=verbose, badval=badval) diff --git a/rubin_sim/maf/web/maf_run_results.py b/rubin_sim/maf/web/maf_run_results.py index 39fd6fca..407fbf16 100644 --- a/rubin_sim/maf/web/maf_run_results.py +++ b/rubin_sim/maf/web/maf_run_results.py @@ -177,7 +177,7 @@ def metric_ids_to_metrics(self, metric_ids, metrics=None): """ if metrics is None: metrics = self.metrics - metrics = metrics[np.in1d(metrics["metric_id"], metric_ids)] + metrics = metrics[np.isin(metrics["metric_id"], metric_ids)] return metrics def metrics_to_metric_ids(self, metrics): @@ -267,7 +267,7 @@ def metrics_with_plot_type(self, plot_type="SkyMap", metrics=None): if metrics is None: metrics = self.metrics # Identify the plots with the right plot_type, get their IDs. - plot_match = self.plots[np.in1d(self.plots["plot_type"], plot_types)] + plot_match = self.plots[np.isin(self.plots["plot_type"], plot_types)] # Convert those potentially matching metricIds to metrics, # using the subset info. metrics = self.metric_ids_to_metrics(plot_match["metric_id"], metrics) @@ -295,7 +295,7 @@ def metrics_with_summary_stat(self, summary_stat_name="Identity", metrics=None): if metrics is None: metrics = self.metrics # Identify the potentially matching stats. - stats = self.stats[np.in1d(self.stats["summary_metric"], summary_stat_name)] + stats = self.stats[np.isin(self.stats["summary_metric"], summary_stat_name)] # Identify the subset of relevant metrics. metrics = self.metric_ids_to_metrics(stats["metric_id"], metrics) # Re-sort metrics because at this point, probably want displayOrder @@ -320,7 +320,7 @@ def metrics_with_stats(self, metrics=None): if metrics is None: metrics = self.metrics # Identify metricIds which are also in stats. - metrics = metrics[np.in1d(metrics["metric_id"], self.stats["metric_id"])] + metrics = metrics[np.isin(metrics["metric_id"], self.stats["metric_id"])] metrics = self.sort_metrics( metrics, order=[ @@ -548,7 +548,7 @@ def order_plots(self, sky_plots): ordered_sky_plots.append(self.plot_dict(np.array([sky_plot]))) elif too_many_plots: - metrics = self.metrics[np.in1d(self.metrics["metric_id"], sky_plots["metric_id"])] + metrics = self.metrics[np.isin(self.metrics["metric_id"], sky_plots["metric_id"])] metrics = self.sort_metrics(metrics, order=["display_order"]) ordered_sky_plots = [] for m in metrics: @@ -569,9 +569,9 @@ def get_sky_maps(self, metrics=None, plot_type="SkyMap"): if metrics is None: metrics = self.metrics # Match the plots to the metrics required. - plot_metric_match = self.plots[np.in1d(self.plots["metric_id"], metrics["metric_id"])] + plot_metric_match = self.plots[np.isin(self.plots["metric_id"], metrics["metric_id"])] # Match the plot type (which could be a list) - plot_match = plot_metric_match[np.in1d(plot_metric_match["plot_type"], plot_type)] + plot_match = plot_metric_match[np.isin(plot_metric_match["plot_type"], plot_type)] return plot_match # Set of methods to deal with summary stats. @@ -631,7 +631,7 @@ def all_stat_names(self, metrics): unique 'summary_metric' names in a default ordering. """ names = np.unique( - self.stats["summary_metric"][np.in1d(self.stats["metric_id"], metrics["metric_id"])] + self.stats["summary_metric"][np.isin(self.stats["metric_id"], metrics["metric_id"])] ) names = list(names) # Add some default sorting. diff --git a/rubin_sim/moving_objects/cheby_values.py b/rubin_sim/moving_objects/cheby_values.py index e724569d..61374436 100644 --- a/rubin_sim/moving_objects/cheby_values.py +++ b/rubin_sim/moving_objects/cheby_values.py @@ -189,7 +189,7 @@ def get_ephemerides(self, times, obj_ids=None, extrapolate=False): else: if isinstance(obj_ids, str) or isinstance(obj_ids, int): obj_ids = np.array([obj_ids]) - obj_match = np.in1d(self.coeffs["obj_id"], obj_ids) + obj_match = np.isin(self.coeffs["obj_id"], obj_ids) ephemerides["obj_id"] = obj_ids # Now find ephemeris values. ephemerides["time"] = np.zeros((len(ephemerides["obj_id"]), ntimes), float) + times diff --git a/rubin_sim/phot_utils/bandpass.py b/rubin_sim/phot_utils/bandpass.py index 46d55361..02abb87a 100644 --- a/rubin_sim/phot_utils/bandpass.py +++ b/rubin_sim/phot_utils/bandpass.py @@ -27,6 +27,11 @@ import warnings import numpy as np + +try: + from numpy import trapezoid as trapezoid +except ImportError: + from numpy import trapz as trapezoid import scipy.interpolate as interpolate from rubin_sim.data import get_data_dir @@ -322,7 +327,7 @@ def sb_tophi(self): # The definition of phi = (Sb/wavelength)/\int(Sb/wavelength)dlambda. self.phi = self.sb / self.wavelen # Normalize phi so that the integral of phi is 1. - norm = np.trapz(self.phi, x=self.wavelen) + norm = trapezoid(self.phi, x=self.wavelen) self.phi = self.phi / norm return diff --git a/rubin_sim/phot_utils/sed.py b/rubin_sim/phot_utils/sed.py index 031aa6f9..ca1523c0 100644 --- a/rubin_sim/phot_utils/sed.py +++ b/rubin_sim/phot_utils/sed.py @@ -78,6 +78,11 @@ import warnings import numpy + +try: + from numpy import trapezoid as trapezoid +except ImportError: + from numpy import trapz as trapezoid from rubin_scheduler.data import get_data_dir from .physical_parameters import PhysicalParameters @@ -1318,7 +1323,7 @@ def calc_flux(self, bandpass, wavelen=None, fnu=None, fill=numpy.nan): if bandpass.phi is None: bandpass.sb_tophi() # Calculate flux in bandpass and return this value. - flux = numpy.trapz(fnu * bandpass.phi, x=wavelen) + flux = trapezoid(fnu * bandpass.phi, x=wavelen) return flux def calc_mag(self, bandpass, wavelen=None, fnu=None, fill=numpy.nan): diff --git a/rubin_sim/skybrightness/interp_components.py b/rubin_sim/skybrightness/interp_components.py index 790eea90..887e9bc2 100644 --- a/rubin_sim/skybrightness/interp_components.py +++ b/rubin_sim/skybrightness/interp_components.py @@ -712,7 +712,7 @@ def _weighting(self, interp_points, values): self.nside, np.pi / 2.0 - interp_points["alt"], interp_points["azRelMoon"] ) - badhp = np.in1d(hpids.ravel(), self.dim_dict["hpid"], invert=True).reshape(hpids.shape) + badhp = np.isin(hpids.ravel(), self.dim_dict["hpid"], invert=True).reshape(hpids.shape) hweights[badhp] = 0.0 norm = np.sum(hweights, axis=0) @@ -777,7 +777,7 @@ def _weighting(self, interp_points, values): use_points["azEclipRelSun"], ) - badhp = np.in1d(hpids.ravel(), self.dim_dict["hpid"], invert=True).reshape(hpids.shape) + badhp = np.isin(hpids.ravel(), self.dim_dict["hpid"], invert=True).reshape(hpids.shape) hweights[badhp] = 0.0 norm = np.sum(hweights, axis=0) diff --git a/tests/maf/test_cadencemetrics.py b/tests/maf/test_cadencemetrics.py index 0e9e6266..8868d14a 100644 --- a/tests/maf/test_cadencemetrics.py +++ b/tests/maf/test_cadencemetrics.py @@ -1,3 +1,4 @@ +import math import unittest import numpy as np @@ -136,7 +137,7 @@ def test_t_gap_metric(self): metric = metrics.TgapsMetric(all_gaps=True, bins=np.arange(1, 100, 10)) result3 = metric.run(data) self.assertEqual(result3[1], 2) - ngaps = np.math.factorial(data.size - 1) + ngaps = math.factorial(data.size - 1) self.assertEqual(np.sum(result3), ngaps) def test_t_gaps_percent_metric(self): @@ -187,7 +188,7 @@ def test_night_gap_metric(self): metric = metrics.NightgapsMetric(all_gaps=True, bins=np.arange(1, 100, 10)) result3 = metric.run(data) self.assertEqual(result3[1], 2) - ngaps = np.math.factorial(data.size - 1) + ngaps = math.factorial(data.size - 1) self.assertEqual(np.sum(result3), ngaps) data = np.zeros(6, dtype=list(zip(names, types))) diff --git a/tests/moving_objects/test_camera.py b/tests/moving_objects/test_camera.py index 5fdaea25..151a8fe1 100644 --- a/tests/moving_objects/test_camera.py +++ b/tests/moving_objects/test_camera.py @@ -5,6 +5,7 @@ from rubin_sim.moving_objects import BaseObs +@unittest.skip("Temporary skip until ephemerides replaced") class TestCamera(unittest.TestCase): def setUp(self): obj_ra = np.array([10.0, 12.1], float) diff --git a/tests/moving_objects/test_chebyfits.py b/tests/moving_objects/test_chebyfits.py index bc14a99b..7dd9a124 100644 --- a/tests/moving_objects/test_chebyfits.py +++ b/tests/moving_objects/test_chebyfits.py @@ -12,6 +12,7 @@ ROOT = os.path.abspath(os.path.dirname(__file__)) +@unittest.skip("Temporary skip until ephemerides replaced") class TestChebyFits(unittest.TestCase): def setUp(self): self.testdir = os.path.join(get_data_dir(), "tests", "orbits_testdata") @@ -135,6 +136,7 @@ def test_write(self): self.assertTrue(os.path.isfile(resid_name)) +@unittest.skip("Temporary skip until ephemerides replaced") class TestRun(unittest.TestCase): def setUp(self): self.testdir = os.path.join(get_data_dir(), "tests", "orbits_testdata") diff --git a/tests/moving_objects/test_chebyvalues.py b/tests/moving_objects/test_chebyvalues.py index 93b55c9f..3fd1847c 100644 --- a/tests/moving_objects/test_chebyvalues.py +++ b/tests/moving_objects/test_chebyvalues.py @@ -14,6 +14,7 @@ ROOT = os.path.abspath(os.path.dirname(__file__)) +@unittest.skip("Temporary skip until ephemerides replaced") class TestChebyValues(unittest.TestCase): def setUp(self): self.testdatadir = os.path.join(get_data_dir(), "tests", "orbits_testdata") @@ -149,6 +150,7 @@ def test_get_ephemerides(self): ) +@unittest.skip("Temporary skip until ephemerides replaced") class TestJPLValues(unittest.TestCase): # Test the interpolation-generated RA/Dec values against JPL # generated RA/Dec values. diff --git a/tests/moving_objects/test_ephemerides.py b/tests/moving_objects/test_ephemerides.py index d5446316..9a30c84f 100644 --- a/tests/moving_objects/test_ephemerides.py +++ b/tests/moving_objects/test_ephemerides.py @@ -9,6 +9,7 @@ from rubin_sim.moving_objects import Orbits, PyOrbEphemerides +@unittest.skip("Temporary skip until ephemerides replaced") class TestPyOrbEphemerides(unittest.TestCase): def setUp(self): self.testdir = os.path.join(get_data_dir(), "tests", "orbits_testdata") @@ -153,6 +154,7 @@ def test_ephemeris(self): np.testing.assert_allclose(ephs_all_kep[column], ephs_all[column], rtol=1e-5, atol=1e-4) +@unittest.skip("Temporary skip until ephemerides replaced") class TestJPLValues(unittest.TestCase): """Test the oorb generated RA/Dec values against JPL generated RA/Dec values.""" diff --git a/tests/phot_utils/test_sed.py b/tests/phot_utils/test_sed.py index 1ed08651..464c82c1 100644 --- a/tests/phot_utils/test_sed.py +++ b/tests/phot_utils/test_sed.py @@ -66,7 +66,7 @@ def test_sed_bandpass_match(self): testsed.resample_sed(wavelen_match=self.testbandpass.wavelen) self.assertEqual(len(wa), 1) self.assertIn("non-overlap", str(wa[-1].message)) - np.testing.assert_equal(testsed.flambda[-1:], np.NaN) + np.testing.assert_equal(testsed.flambda[-1:], np.nan) sedwavelen = np.arange(self.wmin + 50, self.wmax, 1) sedflambda = np.ones(len(sedwavelen)) testsed = Sed(wavelen=sedwavelen, flambda=sedflambda) @@ -74,8 +74,8 @@ def test_sed_bandpass_match(self): testsed.resample_sed(wavelen_match=self.testbandpass.wavelen) self.assertEqual(len(wa), 1) self.assertIn("non-overlap", str(wa[-1].message)) - np.testing.assert_equal(testsed.flambda[0], np.NaN) - np.testing.assert_equal(testsed.flambda[49], np.NaN) + np.testing.assert_equal(testsed.flambda[0], np.nan) + np.testing.assert_equal(testsed.flambda[49], np.nan) def test_rebin(self): """Test that rebinning an SED does not change integrated flux @@ -117,19 +117,19 @@ def test_sed_mag_errors(self): mag = testsed.calc_mag(self.testbandpass) self.assertEqual(len(w), 1) self.assertIn("non-overlap", str(w[-1].message)) - np.testing.assert_equal(mag, np.NaN) + np.testing.assert_equal(mag, np.nan) # Test handling in calc_adu with warnings.catch_warnings(record=True) as w: adu = testsed.calc_adu(self.testbandpass, phot_params=PhotometricParameters()) self.assertEqual(len(w), 1) self.assertIn("non-overlap", str(w[-1].message)) - np.testing.assert_equal(adu, np.NaN) + np.testing.assert_equal(adu, np.nan) # Test handling in calc_flux with warnings.catch_warnings(record=True) as w: flux = testsed.calc_flux(self.testbandpass) self.assertEqual(len(w), 1) self.assertIn("non-overlap", str(w[-1].message)) - np.testing.assert_equal(flux, np.NaN) + np.testing.assert_equal(flux, np.nan) class TestSedName(unittest.TestCase):