From 8f22ef3b59b3cc509f0e173d563e00732a792ef4 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Thu, 28 May 2026 00:58:18 +0200 Subject: [PATCH 01/68] Reduce `OpticalTrain.write_header()` to the bare minimum --- scopesim/optics/optical_train.py | 100 ++++++------------------------- 1 file changed, 18 insertions(+), 82 deletions(-) diff --git a/scopesim/optics/optical_train.py b/scopesim/optics/optical_train.py index 4b3448cb2..422608794 100644 --- a/scopesim/optics/optical_train.py +++ b/scopesim/optics/optical_train.py @@ -505,93 +505,29 @@ def readout(self, filename=None, reset=True, **kwargs): return copy.deepcopy(hduls) def write_header(self, hdulist): - """Write meaningful header to simulation product.""" - # Primary hdu + """Write minimal header to output HDUL. + + This methods exists as a fallback of last resort, if no FITS header + effect is present in the optical train. It should not be expanded, + instead any other keywords required in the output should be included + via those effects. This should only be called for very minimal + instrument setups or during testing and debugging, where most effects + are switched off. + """ + # Primary HDU pheader = hdulist[0].header pheader["DATE"] = datetime.now().isoformat(timespec="seconds") pheader["ORIGIN"] = f"Scopesim {__version__}" + + # These are always present in cmds, so should be save pheader["INSTRUME"] = from_currsys("!OBS.instrument", self.cmds) pheader["INSTMODE"] = ", ".join(from_currsys("!OBS.modes", self.cmds)) - pheader["TELESCOP"] = from_currsys("!TEL.telescope", self.cmds) - pheader["LOCATION"] = from_currsys("!ATMO.location", self.cmds) - - # Source information taken from first only. - # ..todo: What if source is a composite? - srcfield = self._last_source.fields[0] - if type(srcfield).__name__ == "Table": - pheader["SOURCE"] = "Table" - elif type(srcfield).__name__ == "ImageHDU": - if "BG_SURF" in srcfield.header: - pheader["SOURCE"] = srcfield.header["BG_SURF"] - else: - try: - pheader["SOURCE"] = srcfield.header["FILENAME"] - except KeyError: - pheader["SOURCE"] = "ImageHDU" - - # Image hdul - # ..todo: currently only one, update for detector arrays - # ..todo: normalise filenames - some need from_currsys, some need Path(...).name - # this should go into a function so as to reduce clutter here. - iheader = hdulist[1].header - iheader["EXPTIME"] = from_currsys("!OBS.exptime", self.cmds), "[s]" - iheader["DIT"] = from_currsys("!OBS.dit", self.cmds), "[s]" - iheader["NDIT"] = from_currsys("!OBS.ndit", self.cmds) - iheader["BUNIT"] = "e", "per EXPTIME" - iheader["PIXSCALE"] = from_currsys("!INST.pixel_scale", self.cmds), "[arcsec]" - - for eff in self.optics_manager.detector_setup_effects: - efftype = type(eff).__name__ - - if efftype == "DetectorList" and eff.include: - iheader["DETECTOR"] = eff.meta["detector"] - - for eff in self.optics_manager.detector_array_effects: - efftype = type(eff).__name__ - - if (efftype == "DetectorModePropertiesSetter" and - eff.include): - # ..todo: can we write this into currsys? - iheader["DET_MODE"] = (eff.meta["detector_readout_mode"], - "detector readout mode") - iheader["MINDIT"] = from_currsys("!DET.mindit", self.cmds), "[s]" - iheader["FULLWELL"] = from_currsys("!DET.full_well", self.cmds), "[s]" - iheader["RON"] = from_currsys("!DET.readout_noise", self.cmds), "[e]" - iheader["DARK"] = from_currsys("!DET.dark_current", self.cmds), "[e/s]" - - ifilter = 1 # Counts filter wheels - isurface = 1 # Counts surface lists - for eff in self.optics_manager.source_effects: - efftype = type(eff).__name__ - - if efftype == "ADCWheel" and eff.include: - iheader["ADC"] = eff.current_adc.meta["name"] - - if efftype == "FilterWheel" and eff.include: - iheader[f"FILTER{ifilter}"] = (eff.current_filter.meta["name"], - eff.meta["name"]) - ifilter += 1 - - if efftype == "SlitWheel" and eff.include: - iheader["SLIT"] = (eff.current_slit.meta["name"], - eff.meta["name"]) - - if efftype == "PupilTransmission" and eff.include: - iheader["PUPTRANS"] = (from_currsys("!OBS.pupil_transmission", self.cmds), - "cold stop, pupil transmission") - - if efftype == "SkycalcTERCurve" and eff.include: - iheader["ATMOSPHE"] = "Skycalc", "atmosphere model" - iheader["LOCATION"] = eff.meta["location"] - iheader["AIRMASS"] = eff.meta["airmass"] - iheader["TEMPERAT"] = eff.meta["temperature"], "[degC]" - iheader["HUMIDITY"] = eff.meta["humidity"] - iheader["PRESSURE"] = eff.meta["pressure"], "[hPa]" - iheader["PWV"] = eff.meta["pwv"], "precipitable water vapour" - - if efftype == "SurfaceList" and eff.include: - iheader[f"SURFACE{isurface}"] = eff.meta["name"] - isurface += 1 + + # Image HDUs + for hdu in hdulist[1:]: + hdu.header["EXPTIME"] = from_currsys("!OBS.exptime", self.cmds), "[s]" + hdu.header["DIT"] = from_currsys("!OBS.dit", self.cmds), "[s]" + hdu.header["NDIT"] = from_currsys("!OBS.ndit", self.cmds) return hdulist From 1c9fc50ebe9bea3a565adda705cb4d00ccdde43f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2026 15:43:30 +0000 Subject: [PATCH 02/68] Bump actions/checkout from 6 to 7 Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/notebooks_dispatch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/notebooks_dispatch.yml b/.github/workflows/notebooks_dispatch.yml index 70626c3c1..429739e31 100644 --- a/.github/workflows/notebooks_dispatch.yml +++ b/.github/workflows/notebooks_dispatch.yml @@ -29,7 +29,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Install poetry shell: bash From f67816166465b6f0d6a79ed66dd303e4d532eaa9 Mon Sep 17 00:00:00 2001 From: oczoske Date: Thu, 25 Jun 2026 12:53:46 +0200 Subject: [PATCH 03/68] PSF interpolation method and parameters --- scopesim/effects/psfs/discrete.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/scopesim/effects/psfs/discrete.py b/scopesim/effects/psfs/discrete.py index 78a1a42be..92f7cfab6 100644 --- a/scopesim/effects/psfs/discrete.py +++ b/scopesim/effects/psfs/discrete.py @@ -8,7 +8,7 @@ from tqdm.auto import tqdm from scipy.signal import convolve from scipy.ndimage import zoom -from scipy.interpolate import griddata, RegularGridInterpolator +from scipy.interpolate import griddata, RectBivariateSpline, RegularGridInterpolator from astropy import units as u from astropy.io import fits @@ -221,8 +221,9 @@ def get_kernel(self, fov): pix_ratio = kernel_pixel_scale / fov_pixel_scale if abs(pix_ratio - 1) > self.meta["flux_accuracy"]: spline_order = from_currsys( - "!SIM.computing.spline_order", cmds=self.cmds) + self.meta["interp_order"], cmds=self.cmds) self.kernel = _rescale_kernel(self.kernel, pix_ratio, + spline_order=spline_order, image_header=hdr) if ((fov.header["NAXIS1"] < hdr["NAXIS1"]) or @@ -510,7 +511,7 @@ def _make_strehl_map_from_table(tbl, pixel_scale=1*u.arcsec): return map_hdu -def _rescale_kernel(image, scale_factor, method="linear", +def _rescale_kernel(image, scale_factor, spline_order=1, image_header=None): """Rescale `image` by `scale_factor` @@ -523,9 +524,10 @@ def _rescale_kernel(image, scale_factor, method="linear", ratio of input pixel size to output pixel size. Values larger than 1 zoom the image. - method : str - interpolation method to be passed to ``RegularGridInterpolator``. - Default is "linear" + spline_order : int + spline order for interpolation by ``RectBivariateSpline``. + Default is 1 (linear interpolation). The method uses the same order in both + directions (i.e. `kx = ky = spline_order`). image_header : astropy.fits.Header an optional image header with a WCS. If ``None``, a WCS is constructed @@ -537,13 +539,10 @@ def _rescale_kernel(image, scale_factor, method="linear", The output kernel size is always odd to avoid half-pixel shift when convolved with an image with ``mode="same"``. """ - nxin, nyin = image.shape - interp_img = RegularGridInterpolator( - (np.arange(nyin), np.arange(nxin)), - image, method=method, - bounds_error=False, - fill_value=0, - ) + nyin, nxin = image.shape + print("_rescale_kernel: spline_order = ", spline_order) + interp_img = RectBivariateSpline( + np.arange(nxin), np.arange(nyin), image, kx=spline_order, ky=spline_order) # Make the output size odd so that the image remains centred (important # for PSF convolution). @@ -570,10 +569,10 @@ def _rescale_kernel(image, scale_factor, method="linear", outwcs.wcs.crpix = [(nxout + 1)/2, (nyout + 1)/2] outwcs.wcs.crval = [0., 0.] outwcs.wcs.cdelt = inwcs.wcs.cdelt / scale_factor - xout, yout = np.meshgrid(np.arange(nxout), np.arange(nyout)) + xout, yout = np.meshgrid(np.arange(nxout), np.arange(nyout), indexing='ij') xworld, yworld = outwcs.all_pix2world(xout, yout, 0) xin, yin = inwcs.all_world2pix(xworld, yworld, 0) - outimage = interp_img( (yin.ravel(), xin.ravel()) ).reshape((nyout, nxout)) + outimage = interp_img(xin.ravel(), yin.ravel(), grid=False).reshape((nyout, nxout)) logger.info("Interpolating PSF onto %s image", outimage.shape) outimage *= image.sum() / outimage.sum() From 2686075bf87a65cebb7f15f199db35b9351c50b3 Mon Sep 17 00:00:00 2001 From: oczoske Date: Sun, 5 Jul 2026 11:22:14 +0200 Subject: [PATCH 04/68] Interpolation parameters for PSF cube --- scopesim/effects/psfs/discrete.py | 46 ++++++++++++++++++------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/scopesim/effects/psfs/discrete.py b/scopesim/effects/psfs/discrete.py index 92f7cfab6..a5e84a07d 100644 --- a/scopesim/effects/psfs/discrete.py +++ b/scopesim/effects/psfs/discrete.py @@ -166,7 +166,7 @@ class FieldConstantPSF(DiscretePSF): .. versionchanged:: 0.10.0 PSF interpolation can now be limited to the central wavelength by - setting the "!OBS.interp_psf" keyword to False. + setting the "!SIM.psf.interp_psf" keyword to False. .. versionchanged:: 0.11.2 @@ -197,9 +197,11 @@ def get_kernel(self, fov): if ext == self.current_layer_id: return self.kernel + spline_order = from_currsys(self.meta["interp_order"], cmds=self.cmds) + if fov.hdu.header["NAXIS"] == 3: self.current_layer_id = ext - self.make_psf_cube(fov) + self.make_psf_cube(fov, spline_order) return self.kernel self.kernel = self._file[ext].data @@ -220,8 +222,6 @@ def get_kernel(self, fov): # rescaling kept inside loop to avoid rescaling for every fov pix_ratio = kernel_pixel_scale / fov_pixel_scale if abs(pix_ratio - 1) > self.meta["flux_accuracy"]: - spline_order = from_currsys( - self.meta["interp_order"], cmds=self.cmds) self.kernel = _rescale_kernel(self.kernel, pix_ratio, spline_order=spline_order, image_header=hdr) @@ -233,8 +233,19 @@ def get_kernel(self, fov): return np.atleast_2d(self.kernel.squeeze()) - def make_psf_cube(self, fov): - """Create a wavelength-dependent psf cube.""" + def make_psf_cube(self, fov, spline_order=1): + """Create a wavelength-dependent psf cube. + + Parameters + ---------- + fov : FieldOfView + + spline_order : int + spline order for interpolation by ``RectBivariateSpline``. + Default is 1 (linear interpolation). The method uses the same order in both + directions (i.e. `kx = ky = spline_order`). + + """ # Some data from the fov nxfov, nyfov = fov.hdu.header["NAXIS1"], fov.hdu.header["NAXIS2"] fov_pixel_scale = fov.hdu.header["CDELT1"] @@ -246,7 +257,7 @@ def make_psf_cube(self, fov): + fov.hdu.header["CRVAL3"] ) - if not self.cmds.get("!OBS.interp_psf", True): + if not self.cmds.get("!SIM.psf.interp_psf", True): lam = np.array([lam[len(lam)//2]]) # Some data from the psf file @@ -266,13 +277,8 @@ def make_psf_cube(self, fov): psf = psf / psf.sum() # normalisation of the input psf nxin, nyin = psf.shape - # We need linear interpolation to preserve positivity. Might think of - # more elaborate positivity-preserving schemes. - # An alternative would be to use method 'pchip', which uses monotonic - # splines to prevent overshooting (including to negative values) - ipsf = RegularGridInterpolator((np.arange(nyin), np.arange(nxin)), - psf, method='linear', bounds_error=False, - fill_value=0) + ipsf = RectBivariateSpline( + np.arange(nxin), np.arange(nyin), psf, kx=spline_order, ky=spline_order) # adapt the size of the output cube to the FOV's spatial shape psf_maxsize = self.cmds.get("!SIM.computing.psf_maxsize", np.nan) @@ -291,7 +297,7 @@ def make_psf_cube(self, fov): xworld, yworld = cubewcs.all_pix2world(xcube, ycube, 1) outcube = np.zeros((lam.shape[0], nypsf, nxpsf), dtype=np.float32) logger.info("Interpolating PSF onto %s cube", outcube.shape) - + logger.info("Interpolation order %d", spline_order) nlam = len(lam) for i, wave in tqdm(enumerate(lam), desc=" PSF slices", position=2, total=nlam, disable=(nlam <= 1)): @@ -299,11 +305,12 @@ def make_psf_cube(self, fov): psfwcs.wcs.cdelt = [psf_wave_pixscale, psf_wave_pixscale] xpsf, ypsf = psfwcs.all_world2pix(xworld, yworld, 0) - outcube[i,] = (ipsf( (ypsf.ravel(), xpsf.ravel()) ).reshape(outcube[i,].shape) + outcube[i,] = (ipsf( xpsf.ravel(), ypsf.ravel(), grid=False ).reshape(outcube[i,].shape) * fov_pixel_scale**2 / psf_wave_pixscale**2) # .squeeze() gets rid of any axes with length one self.kernel = outcube.reshape((lam.shape[0], nypsf, nxpsf)).squeeze() + # fits.writeto("test_psfcube.fits", data=self.kernel, overwrite=True) def plot(self): @@ -540,7 +547,7 @@ def _rescale_kernel(image, scale_factor, spline_order=1, convolved with an image with ``mode="same"``. """ nyin, nxin = image.shape - print("_rescale_kernel: spline_order = ", spline_order) + interp_img = RectBivariateSpline( np.arange(nxin), np.arange(nyin), image, kx=spline_order, ky=spline_order) @@ -572,9 +579,10 @@ def _rescale_kernel(image, scale_factor, spline_order=1, xout, yout = np.meshgrid(np.arange(nxout), np.arange(nyout), indexing='ij') xworld, yworld = outwcs.all_pix2world(xout, yout, 0) xin, yin = inwcs.all_world2pix(xworld, yworld, 0) - outimage = interp_img(xin.ravel(), yin.ravel(), grid=False).reshape((nyout, nxout)) - logger.info("Interpolating PSF onto %s image", outimage.shape) + logger.info("Interpolating PSF onto %s image", (nyout, nxout)) + logger.info("Interpolation order %d", spline_order) + outimage = interp_img(xin.ravel(), yin.ravel(), grid=False).reshape((nyout, nxout)) outimage *= image.sum() / outimage.sum() return outimage From c252e25c6a5e421317d21c0fa77b8532ce275bfc Mon Sep 17 00:00:00 2001 From: oczoske Date: Sun, 5 Jul 2026 15:15:56 +0200 Subject: [PATCH 05/68] Fix test (unmark one xpass); add default parameters --- scopesim/defaults.yaml | 4 ++++ scopesim/tests/tests_effects/test_ConstPSF.py | 17 +++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/scopesim/defaults.yaml b/scopesim/defaults.yaml index 9c9cf8f75..b561bfa18 100644 --- a/scopesim/defaults.yaml +++ b/scopesim/defaults.yaml @@ -32,6 +32,10 @@ properties : preload_field_of_views : False nan_fill_value: 0. + psf: + inter_psf: True + interp_order: 1 + file : local_packages_path : "./inst_pkgs/" server_base_url : "https://scopesim.univie.ac.at/InstPkgSvr/" diff --git a/scopesim/tests/tests_effects/test_ConstPSF.py b/scopesim/tests/tests_effects/test_ConstPSF.py index 7f6ae9a81..d6270dd1c 100644 --- a/scopesim/tests/tests_effects/test_ConstPSF.py +++ b/scopesim/tests/tests_effects/test_ConstPSF.py @@ -17,7 +17,6 @@ """ import pytest -from pytest import approx import numpy as np from numpy import testing as npt @@ -35,15 +34,17 @@ # pylint: disable=missing-class-docstring, # pylint: disable=missing-function-docstring -@pytest.fixture(scope="function") -def centre_fov(): +@pytest.fixture(name="centre_fov", scope="function") +def fixture_centre_fov(): return _centre_fov() -@pytest.fixture(scope="function") -def const_psf(mock_path): +@pytest.fixture(name="const_psf", scope="function") +def fixture_const_psf(mock_path): + """Instantiate a field constant psf with linear interpolation""" constpsf = FieldConstantPSF( - filename=str(mock_path / "test_ConstPSF.fits")) + filename=str(mock_path / "test_ConstPSF.fits"), + interp_order=1) return constpsf @@ -98,9 +99,9 @@ def test_returns_array_with_single_kernel_from_fov( @pytest.mark.parametrize("scale_factor", ( pytest.param(1/3, marks=pytest.mark.xfail(reason="PSF center off")), 1, - pytest.param(5, marks=pytest.mark.xfail(reason="1x1 array turned into scalar")), + 5 )) - def test_kernel_is_scale_properly_if_cdelts_differ( + def test_kernel_is_scaled_properly_if_cdelts_differ( self, scale_factor, const_psf, From 01be6919c535aa6fe1825e87f2fc1b797b238139 Mon Sep 17 00:00:00 2001 From: oczoske Date: Tue, 7 Jul 2026 16:09:29 +0200 Subject: [PATCH 06/68] Last resort default for interp_order --- scopesim/effects/psfs/discrete.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scopesim/effects/psfs/discrete.py b/scopesim/effects/psfs/discrete.py index a5e84a07d..157f3022b 100644 --- a/scopesim/effects/psfs/discrete.py +++ b/scopesim/effects/psfs/discrete.py @@ -173,6 +173,10 @@ class FieldConstantPSF(DiscretePSF): Fixed handling of background level and rounded edges to avoid visible "squares" in the image. + .. versionchanged:: PLACEHOLDER_NEXT_RELEASE_VERSION + + Interpolation spline order can now be set via "!SIM.psf.interp_order". + """ required_keys = {"filename"} @@ -197,7 +201,13 @@ def get_kernel(self, fov): if ext == self.current_layer_id: return self.kernel - spline_order = from_currsys(self.meta["interp_order"], cmds=self.cmds) + try: + spline_order = from_currsys(self.meta["interp_order"], cmds=self.cmds) + except KeyError: + # Occurs in online notebook test, maybe temporary mismatch with irdb? + logger.warning("PSF parameter interp_order not found, using 1 (linear interpolation).") + spline_order = 1 + if fov.hdu.header["NAXIS"] == 3: self.current_layer_id = ext From 94379f7c55d46db4eae9e2c9013a1b4c4bed0614 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2026 00:46:07 +0200 Subject: [PATCH 07/68] Bump astar-utils from 0.5.3 to 0.6.0 (#951) * Bump astar-utils from 0.5.3 to 0.6.0 Bumps [astar-utils](https://github.com/AstarVienna/astar-utils) from 0.5.3 to 0.6.0. - [Release notes](https://github.com/AstarVienna/astar-utils/releases) - [Commits](https://github.com/AstarVienna/astar-utils/compare/v0.5.3...v0.6.0) --- updated-dependencies: - dependency-name: astar-utils dependency-version: 0.6.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Fix dependabot's mess --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: teutoburg --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 05022a92b..82f96f647 100644 --- a/poetry.lock +++ b/poetry.lock @@ -167,14 +167,14 @@ test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock [[package]] name = "astar-utils" -version = "0.5.3" +version = "0.6.0" description = "Contains commonly-used utilities for AstarVienna's projects." optional = false python-versions = "<3.15,>=3.10" groups = ["main"] files = [ - {file = "astar_utils-0.5.3-py3-none-any.whl", hash = "sha256:2b595b3c5acc20888e6beb8cffcbb5999d17311a358ba97c75d331883a6e0e8e"}, - {file = "astar_utils-0.5.3.tar.gz", hash = "sha256:57423f4c5eeece04b7d92ca6d1f8923f1a25ccfa972f6ddb8194dbcd9197533b"}, + {file = "astar_utils-0.6.0-py3-none-any.whl", hash = "sha256:be49459ad232e5fb49373dcb438524da4172da40317624befe19e80c0f2cc156"}, + {file = "astar_utils-0.6.0.tar.gz", hash = "sha256:67df5126af37c76ed8f357ddc399f7264958148676f0cb7f61e617f440452326"}, ] [package.dependencies] @@ -4652,4 +4652,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.15" -content-hash = "9d466fdf8137d22f15be3a77237a6a8bd2d3f344d34a7c95f18d02f3d56f0192" +content-hash = "c0b084d2a2bfd7cc46d1650fe467a23f41e5a867fec94950a2c03d6515d52139" diff --git a/pyproject.toml b/pyproject.toml index cb4ecb884..2f4d42287 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ dependencies = [ "synphot (>=1.6.1,<2.0.0)", "skycalc-ipy (>=0.7.0,<1.0)", "anisocado (>=0.5.0,<1.0)", - "astar-utils (>=0.5.3,<1.0)" + "astar-utils (>=0.6.0,<1.0)" ] [project.urls] From 50e036066f2cc4d80a1d7abbe739acf407419949 Mon Sep 17 00:00:00 2001 From: "astarvienna-bot[bot]" <217837468+myciapp[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2026 22:46:31 +0000 Subject: [PATCH 08/68] Bumping version from 0.11.5a3 to 0.11.5a4 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2f4d42287..4a413576d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ScopeSim" -version = "0.11.5a3" +version = "0.11.5a4" description = "Generalised telescope observation simulator" license = {text = "GPL-3.0-or-later"} authors = [ From 7e06c276c984171c20529d524bb70ea0d004c704 Mon Sep 17 00:00:00 2001 From: "astarvienna-bot[bot]" <217837468+myciapp[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2026 22:48:50 +0000 Subject: [PATCH 09/68] Bumping version from 0.11.5a4 to 0.11.5a5 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4a413576d..50b6899a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ScopeSim" -version = "0.11.5a4" +version = "0.11.5a5" description = "Generalised telescope observation simulator" license = {text = "GPL-3.0-or-later"} authors = [ From 2cdee39cd77474a81e51e3d00b86d2ea14db2c4e Mon Sep 17 00:00:00 2001 From: oczoske Date: Wed, 8 Jul 2026 08:42:09 +0200 Subject: [PATCH 10/68] Just a typo in doc string --- scopesim/optics/optical_train.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scopesim/optics/optical_train.py b/scopesim/optics/optical_train.py index 422608794..46693e2a0 100644 --- a/scopesim/optics/optical_train.py +++ b/scopesim/optics/optical_train.py @@ -507,7 +507,7 @@ def readout(self, filename=None, reset=True, **kwargs): def write_header(self, hdulist): """Write minimal header to output HDUL. - This methods exists as a fallback of last resort, if no FITS header + This method exists as a fallback of last resort, if no FITS header effect is present in the optical train. It should not be expanded, instead any other keywords required in the output should be included via those effects. This should only be called for very minimal From b190dfd1db5938f4b4c7cf4de2dac886a493ba64 Mon Sep 17 00:00:00 2001 From: oczoske Date: Wed, 8 Jul 2026 14:54:13 +0200 Subject: [PATCH 11/68] More discipline on coordinate order --- scopesim/effects/psfs/discrete.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scopesim/effects/psfs/discrete.py b/scopesim/effects/psfs/discrete.py index 157f3022b..ded2d247f 100644 --- a/scopesim/effects/psfs/discrete.py +++ b/scopesim/effects/psfs/discrete.py @@ -267,7 +267,7 @@ def make_psf_cube(self, fov, spline_order=1): + fov.hdu.header["CRVAL3"] ) - if not self.cmds.get("!SIM.psf.interp_psf", True): + if not self.meta.get("interp_psf", True): lam = np.array([lam[len(lam)//2]]) # Some data from the psf file @@ -285,10 +285,10 @@ def make_psf_cube(self, fov, spline_order=1): psfwcs = WCS(hdr) psf = self._file[ext].data psf = psf / psf.sum() # normalisation of the input psf - nxin, nyin = psf.shape + nyin, nxin = psf.shape ipsf = RectBivariateSpline( - np.arange(nxin), np.arange(nyin), psf, kx=spline_order, ky=spline_order) + np.arange(nyin), np.arange(nxin), psf, kx=spline_order, ky=spline_order) # adapt the size of the output cube to the FOV's spatial shape psf_maxsize = self.cmds.get("!SIM.computing.psf_maxsize", np.nan) @@ -296,7 +296,7 @@ def make_psf_cube(self, fov, spline_order=1): nxpsf = min(nxin, 2 * nxfov + 1, psf_maxsize) nypsf = min(nyin, 2 * nyfov + 1, psf_maxsize) - xcube, ycube = np.meshgrid(np.arange(nxpsf), np.arange(nypsf)) + xcube, ycube = np.meshgrid(np.arange(nxpsf), np.arange(nypsf), sparse=True) cubewcs = WCS(naxis=2) cubewcs.wcs.ctype = ["LINEAR", "LINEAR"] cubewcs.wcs.crval = [0., 0.] @@ -315,7 +315,7 @@ def make_psf_cube(self, fov, spline_order=1): psfwcs.wcs.cdelt = [psf_wave_pixscale, psf_wave_pixscale] xpsf, ypsf = psfwcs.all_world2pix(xworld, yworld, 0) - outcube[i,] = (ipsf( xpsf.ravel(), ypsf.ravel(), grid=False ).reshape(outcube[i,].shape) + outcube[i,] = (ipsf( ypsf, xpsf, grid=False ).reshape(outcube[i,].shape) * fov_pixel_scale**2 / psf_wave_pixscale**2) # .squeeze() gets rid of any axes with length one @@ -559,7 +559,7 @@ def _rescale_kernel(image, scale_factor, spline_order=1, nyin, nxin = image.shape interp_img = RectBivariateSpline( - np.arange(nxin), np.arange(nyin), image, kx=spline_order, ky=spline_order) + np.arange(nyin), np.arange(nxin), image, kx=spline_order, ky=spline_order) # Make the output size odd so that the image remains centred (important # for PSF convolution). @@ -586,13 +586,13 @@ def _rescale_kernel(image, scale_factor, spline_order=1, outwcs.wcs.crpix = [(nxout + 1)/2, (nyout + 1)/2] outwcs.wcs.crval = [0., 0.] outwcs.wcs.cdelt = inwcs.wcs.cdelt / scale_factor - xout, yout = np.meshgrid(np.arange(nxout), np.arange(nyout), indexing='ij') + xout, yout = np.meshgrid(np.arange(nxout), np.arange(nyout), sparse=True) xworld, yworld = outwcs.all_pix2world(xout, yout, 0) xin, yin = inwcs.all_world2pix(xworld, yworld, 0) logger.info("Interpolating PSF onto %s image", (nyout, nxout)) logger.info("Interpolation order %d", spline_order) - outimage = interp_img(xin.ravel(), yin.ravel(), grid=False).reshape((nyout, nxout)) + outimage = interp_img(yin, xin, grid=False).reshape((nyout, nxout)) outimage *= image.sum() / outimage.sum() return outimage From 0cc472b88c7084c9eb507f8cb5e2845ecc43a34f Mon Sep 17 00:00:00 2001 From: teutoburg Date: Sat, 11 Jul 2026 16:25:51 +0200 Subject: [PATCH 12/68] Bump various dependencies Security alerts: - mistune 3.2.1 -> 3.3.3 - soupsieve 2.7 -> 2.8.4 Others: - astropy-iers-data 0.2026.6.15.15.33.16 -> 0.2026.7.6.1.1.20 - beautifulsoup4 4.13.3 -> 4.15.0 - lxml 6.1.0 -> 6.1.1 - matplotlib 3.10.8 -> 3.10.9 - packaging 25.0 -> 26.2 - pillow 12.2.0 -> 12.3.0 - tqdm 4.67.3 -> 4.68.4 - typing-extensions 4.14.0-> 4.16.0 --- poetry.lock | 692 +++++++++++++++++++++++++++---------------------- pyproject.toml | 12 +- 2 files changed, 384 insertions(+), 320 deletions(-) diff --git a/poetry.lock b/poetry.lock index 82f96f647..fd62e1ccd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -238,14 +238,14 @@ typing = ["typing_extensions (>=4.0.0)"] [[package]] name = "astropy-iers-data" -version = "0.2026.6.15.15.33.16" +version = "0.2026.7.6.1.1.20" description = "IERS Earth Rotation and Leap Second tables for the astropy core package" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "astropy_iers_data-0.2026.6.15.15.33.16-py3-none-any.whl", hash = "sha256:5658b05ccf5a0a9a593e719a90a4f09a21e49e2fe745aa38f3c0d49369a516ff"}, - {file = "astropy_iers_data-0.2026.6.15.15.33.16.tar.gz", hash = "sha256:efc9f2b308083434cf07157c85195ffa61fa74ea6c81e5c009588baa2176769b"}, + {file = "astropy_iers_data-0.2026.7.6.1.1.20-py3-none-any.whl", hash = "sha256:7aa50fa7f6d13258357d1e17fa97f3a8c2f04b9a7429d056e986a91cc46576a0"}, + {file = "astropy_iers_data-0.2026.7.6.1.1.20.tar.gz", hash = "sha256:bc38de235802c9f006c8a04693706e2adcc9e7ac1cbf415dc042b5f0ac14f1f3"}, ] [package.extras] @@ -322,18 +322,18 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "beautifulsoup4" -version = "4.13.3" +version = "4.15.0" description = "Screen-scraping library" optional = false python-versions = ">=3.7.0" groups = ["main", "dev", "docs"] files = [ - {file = "beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16"}, - {file = "beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b"}, + {file = "beautifulsoup4-4.15.0-py3-none-any.whl", hash = "sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9"}, + {file = "beautifulsoup4-4.15.0.tar.gz", hash = "sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7"}, ] [package.dependencies] -soupsieve = ">1.2" +soupsieve = ">=1.6.1" typing-extensions = ">=4.0.0" [package.extras] @@ -2004,146 +2004,146 @@ files = [ [[package]] name = "lxml" -version = "6.1.0" +version = "6.1.1" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "lxml-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:41dcc4c7b10484257cbd6c37b83ddb26df2b0e5aff5ac00d095689015af868ec"}, - {file = "lxml-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a31286dbb5e74c8e9a5344465b77ab4c5bd511a253b355b5ca2fae7e579fafec"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1bc4cc83fb7f66ffb16f74d6dd0162e144333fc36ebcce32246f80c8735b2551"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:20cf4d0651987c906a2f5cba4e3a8d6ba4bfdf973cfe2a96c0d6053888ea2ecd"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffb34ea45a82dd637c2c97ae1bbb920850c1e59bcae79ce1c15af531d83e7215"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a1d9b99e5b2597e4f5aed2484fef835256fa1b68a19e4265c97628ef4bf8bcf4"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux_2_28_i686.whl", hash = "sha256:d43aa26dcda363f21e79afa0668f5029ed7394b3bb8c92a6927a3d34e8b610ea"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:6262b87f9e5c1e5fe501d6c153247289af42eb44ad7660b9b3de17baaf92d6f6"}, - {file = "lxml-6.1.0-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d1392c569c032f78a11a25d1de1c43fff13294c793b39e19d84fade3045cbbc3"}, - {file = "lxml-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:045e387d1f4f42a418380930fa3f45c73c9b392faf67e495e58902e68e8f44a7"}, - {file = "lxml-6.1.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9f93d5b8b07f73e8c77e3c6556a3db269918390c804b5e5fcdd4858232cc8f16"}, - {file = "lxml-6.1.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:de550d129f18d8ab819651ffe4f38b1b713c7e116707de3c0c6400d0ef34fbc1"}, - {file = "lxml-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c08da09dc003c9e8c70e06b53a11db6fb3b250c21c4236b03c7d7b443c318e7a"}, - {file = "lxml-6.1.0-cp310-cp310-win32.whl", hash = "sha256:37448bf9c7d7adfc5254763901e2bbd6bb876228dfc1fc7f66e58c06368a7544"}, - {file = "lxml-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:2593a0a6621545b9095b71ad74ed4226eba438a7d9fc3712a99bdb15508cf93a"}, - {file = "lxml-6.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:e80807d72f96b96ad5588cb85c75616e4f2795a7737d4630784c51497beb7776"}, - {file = "lxml-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cec05be8c876f92a5aa07b01d60bbb4d11cfbdd654cad0561c0d7b5c043a61b9"}, - {file = "lxml-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9c03e048b6ce8e77b09c734e931584894ecd58d08296804ca2d0b184c933ce50"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:942454ff253da14218f972b23dc72fa4edf6c943f37edd19cd697618b626fac5"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d036ee7b99d5148072ac7c9b847193decdfeac633db350363f7bce4fff108f0e"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ae5d8d5427f3cc317e7950f2da7ad276df0cfa37b8de2f5658959e618ea8512"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:363e47283bde87051b821826e71dde47f107e08614e1aa312ba0c5711e77738c"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:f504d861d9f2a8f94020130adac88d66de93841707a23a86244263d1e54682f5"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:23a5dc68e08ed13331d61815c08f260f46b4a60fdd1640bbeb82cf89a9d90289"}, - {file = "lxml-6.1.0-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f15401d8d3dbf239e23c818afc10c7207f7b95f9a307e092122b6f86dd43209a"}, - {file = "lxml-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fcf3da95e93349e0647d48d4b36a12783105bcc74cb0c416952f9988410846a3"}, - {file = "lxml-6.1.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0d082495c5fcf426e425a6e28daaba1fcb6d8f854a4ff01effb1f1f381203eb9"}, - {file = "lxml-6.1.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:e3c4f84b24a1fcba435157d111c4b755099c6ff00a3daee1ad281817de75ed11"}, - {file = "lxml-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:976a6b39b1b13e8c354ad8d3f261f3a4ac6609518af91bdb5094760a08f132c4"}, - {file = "lxml-6.1.0-cp311-cp311-win32.whl", hash = "sha256:857efde87d365706590847b916baff69c0bc9252dc5af030e378c9800c0b10e3"}, - {file = "lxml-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:183bfb45a493081943be7ea2b5adfc2b611e1cf377cefa8b8a8be404f45ef9a7"}, - {file = "lxml-6.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:19f4164243fc206d12ed3d866e80e74f5bc3627966520da1a5f97e42c32a3f39"}, - {file = "lxml-6.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d2f17a16cd8751e8eb233a7e41aecdf8e511712e00088bf9be455f604cd0d28d"}, - {file = "lxml-6.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0cea5b1d3e6e77d71bd2b9972eb2446221a69dc52bb0b9c3c6f6e5700592d93"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc46da94826188ed45cb53bd8e3fc076ae22675aea2087843d4735627f867c6d"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9147d8e386ec3b82c3b15d88927f734f565b0aaadef7def562b853adca45784a"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5715e0e28736a070f3f34a7ccc09e2fdcba0e3060abbcf61a1a5718ff6d6b105"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4937460dc5df0cdd2f06a86c285c28afda06aefa3af949f9477d3e8df430c485"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc783ee3147e60a25aa0445ea82b3e8aabb83b240f2b95d32cb75587ff781814"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:40d9189f80075f2e1f88db21ef815a2b17b28adf8e50aaf5c789bfe737027f32"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:05b9b8787e35bec69e68daf4952b2e6dfcfb0db7ecf1a06f8cdfbbac4eb71aad"}, - {file = "lxml-6.1.0-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0f0f08beb0182e3e9a86fae124b3c47a7b41b7b69b225e1377db983802404e54"}, - {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73becf6d8c81d4c76b1014dbd3584cb26d904492dcf73ca85dc8bff08dcd6d2d"}, - {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1ae225f66e5938f4fa29d37e009a3bb3b13032ac57eb4eb42afa44f6e4054e69"}, - {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:690022c7fae793b0489aa68a658822cea83e0d5933781811cabbf5ea3bcfe73d"}, - {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:63aeafc26aac0be8aff14af7871249e87ea1319be92090bfd632ec68e03b16a5"}, - {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:264c605ab9c0e4aa1a679636f4582c4d3313700009fac3ec9c3412ed0d8f3e1d"}, - {file = "lxml-6.1.0-cp312-cp312-win32.whl", hash = "sha256:56971379bc5ee8037c5a0f09fa88f66cdb7d37c3e38af3e45cf539f41131ac1f"}, - {file = "lxml-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:bba078de0031c219e5dd06cf3e6bf8fb8e6e64a77819b358f53bb132e3e03366"}, - {file = "lxml-6.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:c3592631e652afa34999a088f98ba7dfc7d6aff0d535c410bea77a71743f3819"}, - {file = "lxml-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a0092f2b107b69601adf562a57c956fbb596e05e3e6651cabd3054113b007e45"}, - {file = "lxml-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fc7140d7a7386e6b545d41b7358f4d02b656d4053f5fa6859f92f4b9c2572c4d"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:419c58fc92cc3a2c3fa5f78c63dbf5da70c1fa9c1b25f25727ecee89a96c7de2"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:37fabd1452852636cf38ecdcc9dd5ca4bba7a35d6c53fa09725deeb894a87491"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2853c8b2170cc6cd54a6b4d50d2c1a8a7aeca201f23804b4898525c7a152cfc"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8e369cbd690e788c8d15e56222d91a09c6a417f49cbc543040cba0fe2e25a79e"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e69aa6805905807186eb00e66c6d97a935c928275182eb02ee40ba00da9623b2"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:4bd1bdb8a9e0e2dd229de19b5f8aebac80e916921b4b2c6ef8a52bc131d0c1f9"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:cbd7b79cdcb4986ad78a2662625882747f09db5e4cd7b2ae178a88c9c51b3dfe"}, - {file = "lxml-6.1.0-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:43e4d297f11080ec9d64a4b1ad7ac02b4484c9f0e2179d9c4ef78e886e747b88"}, - {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cc16682cc987a3da00aa56a3aa3075b08edb10d9b1e476938cfdbee8f3b67181"}, - {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d6d8efe71429635f0559579092bb5e60560d7b9115ee38c4adbea35632e7fa24"}, - {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e39ab3a28af7784e206d8606ec0e4bcad0190f63a492bca95e94e5a4aef7f6e"}, - {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:9eb667bf50856c4a58145f8ca2d5e5be160191e79eb9e30855a476191b3c3495"}, - {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7f4a77d6f7edf9230cee3e1f7f6764722a41604ee5681844f18db9a81ea0ec33"}, - {file = "lxml-6.1.0-cp313-cp313-win32.whl", hash = "sha256:28902146ffbe5222df411c5d19e5352490122e14447e98cd118907ee3fd6ee62"}, - {file = "lxml-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:4a1503c56e4e2b38dc76f2f2da7bae69670c0f1933e27cfa34b2fa5876410b16"}, - {file = "lxml-6.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:e0af85773850417d994d019741239b901b22c6680206f46a34766926e466141d"}, - {file = "lxml-6.1.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:ab863fd37458fed6456525f297d21239d987800c46e67da5ef04fc6b3dd93ac8"}, - {file = "lxml-6.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6fd8b1df8254ff4fd93fd31da1fc15770bde23ac045be9bb1f87425702f61cc9"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:47024feaae386a92a146af0d2aeed65229bf6fff738e6a11dda6b0015fb8fd03"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3f00972f84450204cd5d93a5395965e348956aaceaadec693a22ec743f8ae3eb"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97faa0860e13b05b15a51fb4986421ef7a30f0b3334061c416e0981e9450ca4c"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:972a6451204798675407beaad97b868d0c733d9a74dafefc63120b81b8c2de28"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fe022f20bc4569ec66b63b3fb275a3d628d9d32da6326b2982584104db6d3086"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:75c4c7c619a744f972f4451bf5adf6d0fb00992a1ffc9fd78e13b0bc817cc99f"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:3648f20d25102a22b6061c688beb3a805099ea4beb0a01ce62975d926944d292"}, - {file = "lxml-6.1.0-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:77b9f99b17cbf14026d1e618035077060fc7195dd940d025149f3e2e830fbfcb"}, - {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32662519149fd7a9db354175aa5e417d83485a8039b8aaa62f873ceee7ea4cad"}, - {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:73d658216fc173cf2c939e90e07b941c5e12736b0bf6a99e7af95459cfe8eabb"}, - {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ac4db068889f8772a4a698c5980ec302771bb545e10c4b095d4c8be26749616f"}, - {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:45e9dfbd1b661eb64ba0d4dbe762bd210c42d86dd1e5bd2bdf89d634231beb43"}, - {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:89e8d73d09ac696a5ba42ec69787913d53284f12092f651506779314f10ba585"}, - {file = "lxml-6.1.0-cp314-cp314-win32.whl", hash = "sha256:ebe33f4ec1b2de38ceb225a1749a2965855bffeef435ba93cd2d5d540783bf2f"}, - {file = "lxml-6.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:398443df51c538bd578529aa7e5f7afc6c292644174b47961f3bf87fe5741120"}, - {file = "lxml-6.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:8c8984e1d8c4b3949e419158fda14d921ff703a9ed8a47236c6eb7a2b6cb4946"}, - {file = "lxml-6.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1081dd10bc6fa437db2500e13993abf7cc30716d0a2f40e65abb935f02ec559c"}, - {file = "lxml-6.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:dabecc48db5f42ba348d1f5d5afdc54c6c4cc758e676926c7cd327045749517d"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e3dd5fe19c9e0ac818a9c7f132a5e43c1339ec1cbbfecb1a938bd3a47875b7c9"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9e7b0a4ca6dcc007a4cef00a761bba2dea959de4bd2df98f926b33c92ca5dfb9"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d27bbe326c6b539c64b42638b18bc6003a8d88f76213a97ac9ed4f885efeab7"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4e425db0c5445ef0ad56b0eec54f89b88b2d884656e536a90b2f52aecb4ca86"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b89b098105b8599dc57adac95d1813409ac476d3c948a498775d3d0c6124bfb"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:c4a699432846df86cc3de502ee85f445ebad748a1c6021d445f3e514d2cd4b1c"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:30e7b2ed63b6c8e97cca8af048589a788ab5c9c905f36d9cf1c2bb549f450d2f"}, - {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:022981127642fe19866d2907d76241bb07ed21749601f727d5d5dd1ce5d1b773"}, - {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:23cad0cc86046d4222f7f418910e46b89971c5a45d3c8abfad0f64b7b05e4a9b"}, - {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:21c3302068f50d1e8728c67c87ba92aa87043abee517aa2576cca1855326b405"}, - {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:be10838781cb3be19251e276910cd508fe127e27c3242e50521521a0f3781690"}, - {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2173a7bffe97667bbf0767f8a99e587740a8c56fdf3befac4b09cb29a80276fd"}, - {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c6854e9cf99c84beb004eecd7d3a3868ef1109bf2b1df92d7bc11e96a36c2180"}, - {file = "lxml-6.1.0-cp314-cp314t-win32.whl", hash = "sha256:00750d63ef0031a05331b9223463b1c7c02b9004cef2346a5b2877f0f9494dd2"}, - {file = "lxml-6.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:80410c3a7e3c617af04de17caa9f9f20adaa817093293d69eae7d7d0522836f5"}, - {file = "lxml-6.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:26dd9f57ee3bd41e7d35b4c98a2ffd89ed11591649f421f0ec19f67d50ec67ac"}, - {file = "lxml-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b6c2f225662bc5ad416bdd06f72ca301b31b39ce4261f0e0097017fc2891b940"}, - {file = "lxml-6.1.0-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a86f06f059e22a0d574990ee2df24ede03f7f3c68c1336293eee9536c4c776cd"}, - {file = "lxml-6.1.0-cp38-cp38-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:468479e52ecf3ec23799c863336d02c05fc2f7ffd1a1424eeeb9a28d4eb69d13"}, - {file = "lxml-6.1.0-cp38-cp38-manylinux_2_28_i686.whl", hash = "sha256:a02ca8fe48815bddcfca3248efe54451abb9dbf2f7d1c5744c8aa4142d476919"}, - {file = "lxml-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bb40648d96157f9081886defe13eac99253e663be969ff938a9289eff6e47b72"}, - {file = "lxml-6.1.0-cp38-cp38-win32.whl", hash = "sha256:1dd6a1c3ad4cb674f44525d9957f3e9c209bb6dd9213245195167a281fcc2bdc"}, - {file = "lxml-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:4e2c54d6b47361d0f1d3bc8d4e082ad87201e56ccdcca4d3b9ee3644ff595ec8"}, - {file = "lxml-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:920354904d1cb86577d4b3cfe2830c2dbe81d6f4449e57ada428f1609b5985f7"}, - {file = "lxml-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c871299c595ee004d186f61840f0bfc4941aa3f17c8ba4a565ead7e4f4f820ee"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d0d799ff958655781296ec870d5e2448e75150da2b3d07f13ff5b0c2c35beefd"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ba11752e346bd804ea312ec2eea2532dfa8b8d3261d81a32ef9e6ab16256280"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:26c5272c6a4bf4cf32d3f5a7890c942b0e04438691157d341616d02cca74d4bd"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c53fa3a5a52122d590e847a57ccf955557b9634a7f99ff5a35131321b0a85317"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux_2_28_i686.whl", hash = "sha256:76b958b4ea3104483c20f74866d55aa056546e15ebe83dd7aecd63698f43b755"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:8c11b984b5ce6add4dccc7144c7be5d364d298f15b0c6a57da1991baedc750ce"}, - {file = "lxml-6.1.0-cp39-cp39-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d3829a6e6fd550a219564912d4002c537f65da4c6ae4e093cc34462f4fa027ad"}, - {file = "lxml-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:52b0ac6903cf74ebf997eb8c682d2fbac7d1ab7e4c552413eec55868a9b73f39"}, - {file = "lxml-6.1.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:29f5c00cb7d752bce2c70ebd2d31b0a42f9499ffdd3ecb2f31a5b73ee43031ad"}, - {file = "lxml-6.1.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:c748ebcb6877de89f48ab90ca96642ac458fff5dec291a2b9337cd4d0934e383"}, - {file = "lxml-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:08950a23f296b3f83521577274e3d3b0f3d739bf2e68d01a752e4288bc50d286"}, - {file = "lxml-6.1.0-cp39-cp39-win32.whl", hash = "sha256:11a873c77a181b4fef9c2e357d08ed399542c2af1390101da66720a19c7c9618"}, - {file = "lxml-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:81ff55c70b67d19d52b6fd118a114c0a4c97d799cd3089ff9bd9e2ff4b414ee2"}, - {file = "lxml-6.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:481d6e2104285d9add34f41b42b247b76b61c5b5c26c303c2e9707bbf8bd9a64"}, - {file = "lxml-6.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:546b66c0dd1bb8d9fa89d7123e5fa19a8aff3a1f2141eb22df96112afb17b842"}, - {file = "lxml-6.1.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5cfa1a34df366d9dc0d5eaf420f4cf2bb1e1bebe1066d1c2fc28c179f8a4004c"}, - {file = "lxml-6.1.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db88156fcf544cdbf0d95588051515cfdfd4c876fc66444eb98bceb5d6db76de"}, - {file = "lxml-6.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:07f98f5496f96bf724b1e3c933c107f0cbf2745db18c03d2e13a291c3afd2635"}, - {file = "lxml-6.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4642e04449a1e164b5ff71ffd901ddb772dfabf5c9adf1b7be5dffe1212bc037"}, - {file = "lxml-6.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7da13bb6fbadfafb474e0226a30570a3445cfd47c86296f2446dafbd77079ace"}, - {file = "lxml-6.1.0.tar.gz", hash = "sha256:bfd57d8008c4965709a919c3e9a98f76c2c7cb319086b3d26858250620023b13"}, + {file = "lxml-6.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:09dd5b7075dc2f7709654a46543ba1ea3c2e217b2ed8fbd413a8a945a0f40f60"}, + {file = "lxml-6.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f6ac4ef4d82dff54670227a69c67782ae0b811b5cf6b17954f1e8f7502fc0d1d"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:556e94a63c9b04716f8e4de2abb65775061f846e89331b6c5be79183a24f98ea"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5c6bf403fbb3b3e348a561a5f4f0b9961835657981c802a1df03653eef8a9074"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1dde6131244bba38a17c745836ba190bc753fd73c9291666287fd0a3fa3dcf30"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98fc784c2c1440667aeedf8465bdfe10208acf0ead656a2c68627299f546b315"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux_2_28_i686.whl", hash = "sha256:add8cf6ddf9a65116119a28ece0f7886e30af27ba724a7594305f1d1b58a92a1"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:cf9d57306d848218f3601fee7601fab1a327c942d56e2e97610583cb4dd74206"}, + {file = "lxml-6.1.1-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88136950da4d13c318bde414ce10219931937851327f44328f2df4d2c4614067"}, + {file = "lxml-6.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cecdd5dfdc87b1fd87dbf81d4b037a544f47f4c744200a67013771682d67686a"}, + {file = "lxml-6.1.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cd312b9692e831d2ffcad61eab31d91d4b4655a962e61de8fb410472cbcd37aa"}, + {file = "lxml-6.1.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:5b7328b46d49fc9477d91ae8f6d55340347d827b7734ba3ea33faae0efef1383"}, + {file = "lxml-6.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37a58976370f36d9329d118ad0b953c5aeb9119ac9c6a4e258942a225d0573a1"}, + {file = "lxml-6.1.1-cp310-cp310-win32.whl", hash = "sha256:cea3f4c1af79af13cdb2da0c028111d8f8522d4f22a000c82385535f24e5cf3a"}, + {file = "lxml-6.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:3abf332af33a74288675d936fe861fd4344da0dd6622193fbc4f2bfbb35536b5"}, + {file = "lxml-6.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:8dadbe5b217ff35b6a8d16610dd710219b59b76d13f0e3f0d9f36786206e4485"}, + {file = "lxml-6.1.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:53b7d2b7a10b1c35c0a5e21e9224accf60c1bbfba523990732e521b2b73adef2"}, + {file = "lxml-6.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3f333630ab480244a1bff72043e511a91eb22e7595dead8653ee5612dd8f3d"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a4bbea04c97f6d78a48e3fbc1cb9116d2780b1b39e03a23f6eb9b603fd61f510"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db1d75f6617a49c1c01bc7023713e0ff59ab32c9579ae62a7674c0e34f3b0b0a"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a12689be69a28ddaa0ab99a5a1137da2afd5f8f16df7b5680b66f616d3eda1d"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b73c339ae29b90fd2d06e58ebd555a751bde9cd6bbd36cc0281b9a2c94e9d8"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:752d3bbfe874715ccd0aec7f88d7fc623c0f1fd7aa7b3238a084e017bad2a009"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:6b1761fbf9ec984e2e9d9c589ef5f5fd684b7c19f92aadd567a26c5224958db6"}, + {file = "lxml-6.1.1-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d680fbcb768404c601ecb43519ecd8461f6954cb11c06a78962f666832ccfca8"}, + {file = "lxml-6.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:162af1091cd785f2f27e62d3547ae9bc58ec5c86dd314d67021fd02463708d83"}, + {file = "lxml-6.1.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e9308ff8241c532df3f3e570f9a5aeed6c853f888512ba4b75638d7c11c95ef6"}, + {file = "lxml-6.1.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5f6994074ebae6ffb04447268e37dc16edc304f9859cf91acb86e0af6c1b395c"}, + {file = "lxml-6.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:80c2dfadb855da477cf73373ad29a333535dedb9b12bad02c9814c8e2b43bf08"}, + {file = "lxml-6.1.1-cp311-cp311-win32.whl", hash = "sha256:30a89d3ac8faec007453fb541f3f46807eeec88edd5826f6e3fe001752a2c621"}, + {file = "lxml-6.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:abbefa31eee84842140f67acef1c828e28bba8bbf0c3bc6e5492a9af88152c28"}, + {file = "lxml-6.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:dcb292aa7fe485ceff7af4f92e46c5af397daec5dff64871a528f0fc47a3cc5b"}, + {file = "lxml-6.1.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:104c09bda8d2a562824c0e319d0768ce26a779b7601e0931d33b09b53c392ef7"}, + {file = "lxml-6.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:25c6997a9a534e016695a0ba06b2f07945de682731ff01065b6d5a4474179da1"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c921ba5c51e4e9f63b8b00267d06566e1f63407408a0496da2d1d0bfc819c7fc"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:54a7f95e4de5fb94e2f9f4b9055c6ba33bf3d628fd77a1d647c5923caa2cdcdc"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f2ec43df44b1f76249ee0a615334f9b5b060e1c8bd90e706dad2d14d02f383"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:70ef8a7e102a1508f8121aae5b0867abd663f72c14f0a9c937e6554cb4587b7b"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ebe6af670449830d6d9b752c256a983291c766a1365ba5d5460048f9e33a7818"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:27acc820660aaffa4f7c087f29120e12980f7779d56d8492d263170111284740"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:1db753c9115ec7100d073b744d17e25e88a8f90f5c39b2f5dd878149af59671f"}, + {file = "lxml-6.1.1-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4f469aebd783bb741c2ecb2a681008fd26bfe5c16a9a72ed5467f834e810df2"}, + {file = "lxml-6.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:766b010012d59470072c1816b5b6c69f1d243e5db36ea5968e94accf430a4635"}, + {file = "lxml-6.1.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b8d812c6011c08b8111a15e54dd990b8923692d80adf35488bee34026c35accf"}, + {file = "lxml-6.1.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:fe0306bd29505a9177aac19f1877174b0e7422c222a59f70b2cd41633448c3dc"}, + {file = "lxml-6.1.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5ba186ad207446c65d3bb3d3e0412b032b1d9f595e59861e2354798c5703d955"}, + {file = "lxml-6.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aa366a1e55b8ebfe8ca8ddc3cfe75c8ebade181aeb0f661d0cb05986b647f72a"}, + {file = "lxml-6.1.1-cp312-cp312-win32.whl", hash = "sha256:126c93f7f56f0eda92f6d8c619edc463a4f23d9252f1c9d0405a76f25fa9f11a"}, + {file = "lxml-6.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:26e6eda8d38c1fcab1090dd196ee87cbd13788e531937610e2589085de074e77"}, + {file = "lxml-6.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:6540377fbd53fe1b629172288c464fb18db11ce1fa7dc15891da10aa9dcc3e7f"}, + {file = "lxml-6.1.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:68a9198d0fc122d14bb76837de9aa80cf84caed990b5b237f532ed87d3706736"}, + {file = "lxml-6.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7d47866cb32fb503450b6edc9df355d10dc49836af2e89901bd6ac6b0896d9d9"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb7c9811bfaa8b1ed5ed319f5d370dfbcaa59d52ea64be2a5a85e18195930354"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:762ff394d5bd56da0cf034a23dcce4e13923f15321a2adfa2ac00201dc6d3fca"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a088f287f7d8275a33c07f2cac6c50b9319309a0200a39e7e75d80c707723099"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e902da4b04e6b52e5893900d4b8ab46068f75f3561f01bf1080957f9fd932ed6"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d4962d4c66bf830a7e59ed6cfc17d148149898a3aefa8ec6e59763e6e3ed085"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:581d4c8ae690a6609e64862dd6b7c2489635c2d13907fc2b20f2bc200ff1d21e"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:876e1ff5930ed8bf295ec5ef9a8155e9b6b1876bbf1deed8b3a8069311875a8f"}, + {file = "lxml-6.1.1-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9eb9b5a968f6e0f6d640092a567e14529ff8cea2e29d00da6f78a79fa49f013c"}, + {file = "lxml-6.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:aa49e06d94aba782c6a02eecb7e507969e7e7a41b267f1b359bb35585f295d5b"}, + {file = "lxml-6.1.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:70cdfd80589d59e43e18005dd7244e8895e93db8ab6a620b7e23df5445a4e3d2"}, + {file = "lxml-6.1.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:aad9aa39483ed8ec44d6d2e59e5b98a0d80676ef0d92f44bfc374836111f62f5"}, + {file = "lxml-6.1.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:d49514be2f28d895c38cf9d2b72d7b9a07d00314519f456c0b50b53cfcf4c785"}, + {file = "lxml-6.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:47402e62c52ff5988c1e8c6c63177f5708bccf48e366dea4e3dcf1e645e04947"}, + {file = "lxml-6.1.1-cp313-cp313-win32.whl", hash = "sha256:3483644525531e1d5762b0c44a8e18b6efba321b6dcf8a8952de10b037618bca"}, + {file = "lxml-6.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:a10bd2fd62e8ce916ececb342f348f190724a098c1faa056fdfb2a22ad5e8660"}, + {file = "lxml-6.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:424aa57aca0897eb922aef34395bd1289b3b6f04e6bae20ea123c0c7e333cffc"}, + {file = "lxml-6.1.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:19b7ab10b210b0b3ad7985d9ac4eb66ab09a90b20fe6e2f7ba55d01a234345d0"}, + {file = "lxml-6.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c08e5c694306507275f2290073350c4f32e383db15213b2c69e7ff39c1193840"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:74a9717fd0d82effef5c2854f0d917231d5324b5a3eb7275c43ac9fa32f97a14"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efe0374196335f93b53269acd811b944f2e6bdc88e8894f214bd636455484909"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac931cdc9442c1763b8a8f6cd62c0c938737eafc5be75eff88df55fc73bc0d00"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:aee395f5d0927f947758b4ec119fd5fc8ec71f07a1c5c52077b30b04c0fa6955"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9395002973c827b3ed67db77e6ec09f092919a587022174554096a269378fb13"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:73bc2086f141224ebddb7fc5c6a36ca58b31b94b561e1dfe8e073e3270fad1e7"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:3779def59032b81e44a5f70096ef6bf2082f8d901937dca354474ba09782e245"}, + {file = "lxml-6.1.1-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:86c89b9d55ebf820ad7c90bc533410f0d098054f293351f10603c0c46ff598f5"}, + {file = "lxml-6.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19607c6bbff2a44cf3fe8250abccd20942d3462473e0a721d01d379ed017e462"}, + {file = "lxml-6.1.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c6ed5141a5c7507cf3ee76bd363b0d6f801e3321adc35b5d825a23115faa5465"}, + {file = "lxml-6.1.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:62aeb7e85b5d60320b9d77eef2e773994e2c0ce10121b277e0a19804e1654a5a"}, + {file = "lxml-6.1.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b1b963fd8f5caa68e99dfae060d54de1fe9cba899b8718b44a00cdca53c3e590"}, + {file = "lxml-6.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63876be28efefa04a1df615b46770e82042cce445cfdce55160522f57b231ccb"}, + {file = "lxml-6.1.1-cp314-cp314-win32.whl", hash = "sha256:7f7a92e8583f06b1fd49d01158143b8461cfcd135dcb10ec807270a3051bd603"}, + {file = "lxml-6.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:b2d444f2e66624d68e9c6b211e28a76e22fff5fcabcfff4deac18b529b7d4137"}, + {file = "lxml-6.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:3fd9728a2735fda14f4e8235830c86b539e9661e849665bf926d3f867943b4bf"}, + {file = "lxml-6.1.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:787b2496d0dbe8cd180984e8d29e3a6f76e7ea34db781cb3bd55e4ba1ef8b4ee"}, + {file = "lxml-6.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2c8daa471358dc2d6fcf02165e80ec68f77871a286df95bc5cc3816153b0fd2c"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:acd7d70b64c0aae0c7922cca83d288a16f5f6da523637697872253415269baef"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4f0dd2f01f9f8a89f565d000e03abcf0a13d692a346c8d22f628d49af098777a"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b7e8a14c8634bf6f7a568634cb395305a6d964aeb5b7ee32248094bed3a7e2c"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:86281fbdd6a8162756f8d603f37e3435bfa38043adb79c6dc6a2dfee065e7525"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5d7152ec39ca7c402d8fb9bad86140a15b9503bd0c54484e3f1bbe3dd37ceca"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:88d8cb75b9d82858497a5393e3c63cfbf03035225e4b35a49ed7ccb151e4dc0e"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f64ec5397ea6a41fc1b4af0380d79b44a755b5531dcaccd9940fb260dca93038"}, + {file = "lxml-6.1.1-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d34bbf07dbc7ca5970671b1512e928991fb5e9d95365636c9b2d8b4f53af405e"}, + {file = "lxml-6.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:17e0e18d4ad8adbd0399291bc44845b69d9dd68439a3cdebdf35ff902ec05072"}, + {file = "lxml-6.1.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:3ab541146f1f6968c462d6c2ac495148e8cdba2f8347700b2141b6ec5a75bf52"}, + {file = "lxml-6.1.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2a0217714657e023ef4293500f65aa20fce6164c8fd6b08fa5bd4a859fb14b9b"}, + {file = "lxml-6.1.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:05a82eb6e1530a64f26225b55cbd178113bd0b5af1c2b625f25e5296742c26d2"}, + {file = "lxml-6.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9e36f163528fc50cbef305f02a5fd66d404edf7049cdaff211dbc2cba5a7013e"}, + {file = "lxml-6.1.1-cp314-cp314t-win32.whl", hash = "sha256:649dda677cf3bd6ac9ae14007ba0c824ded8ce5808b53fc7431d9140399118c1"}, + {file = "lxml-6.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:793033d6c5cdf33a573f910d9bea14ef8f5771820411d118da8e1182edb53d5e"}, + {file = "lxml-6.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:58bb955caba94e467d2a96da17660d2d704e0675894cba21ab8a775b8621fd1c"}, + {file = "lxml-6.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6689e828a94eee4f139408c337bb198e014724bb8a8c26d3cfac49d119ed69a6"}, + {file = "lxml-6.1.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bdebcc8a75d38c7598dfb2c9ed852d7a9eb4a10d6e2d0764b919b802bf32ac88"}, + {file = "lxml-6.1.1-cp38-cp38-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8be8ad51249698103d24b0571df35a10990fbe93dd043b6c024172189485f5e3"}, + {file = "lxml-6.1.1-cp38-cp38-manylinux_2_28_i686.whl", hash = "sha256:76447f65250ed2501ead1a1552f5ce8edff159a86f308348e6a9c4acb5e1f1b4"}, + {file = "lxml-6.1.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ffecec8eb889b58ba9be5b95fb1cc78e22ea8eedea38e8736a1568fe1979250e"}, + {file = "lxml-6.1.1-cp38-cp38-win32.whl", hash = "sha256:c674693f055fa2495de12292cb45e9944199d8eaef5a2dec45175c7c61cb73e3"}, + {file = "lxml-6.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:55b03549819867ea141c0202242c4816c82e52ec36e7e648db9d8da5a3dc3ed6"}, + {file = "lxml-6.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c9f79d5325907f13e1be0b3e4dacc1049d1dffc4aeee3c995284bea5fe0fab7d"}, + {file = "lxml-6.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:83b6b30eb131da7a75b601f28c5d6971e6ed3e887919bf6b6a1ad3c2df289080"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:441dd227fa0690eb9fc81edabc63cdcefc212bba99b906dcf6e32cc1a9d3e533"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e07c65f443c887bbcf31cc1771d932ecc192a5273943589b3c7572b749f1ffb2"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5bec7d03d78d853597d6107854c2310ce3f761fd218fe9fe91d5101fcf6c2efe"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f76acfb5f68ba982635a53fd985a8044be98a35b43232c2a1ee235ffab3e1dd"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux_2_28_i686.whl", hash = "sha256:8d43ca737b20e106e4aebc42b2f3ae19f00ba63d7eb731698ee083d72d15646f"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:32ab449a5486f6c758e849bb86710d0e45edc24a04e250c01555f8f5653958f8"}, + {file = "lxml-6.1.1-cp39-cp39-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53c909b62a0532183542fed00c5a7218258c56292d409bc789886fe1cb04c438"}, + {file = "lxml-6.1.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:640f97d43d867bcb9c75b3af013b64850756b746cb6bce8ace83b70da3abba9d"}, + {file = "lxml-6.1.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:469e3618338bd7ab5beb412d2439825479fcf0dab99e394ca563dbc4eaf6c834"}, + {file = "lxml-6.1.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:aae97dfdb60715c164419ac2532a76d013c3918a665eb6cb7288098b5f349aaf"}, + {file = "lxml-6.1.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c9a4b821dc7055bf9e05ff5719e18ec501f75c0f0bbfabd573b277559780833d"}, + {file = "lxml-6.1.1-cp39-cp39-win32.whl", hash = "sha256:639f6c857d91d9be29bd7502348d6736dab168b54b5158cd899abf11684dc186"}, + {file = "lxml-6.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:34c2d737beabfe35baada43941ed519251e9a12e779031496bcd5d539fcfd730"}, + {file = "lxml-6.1.1-cp39-cp39-win_arm64.whl", hash = "sha256:07a4a68e286ee7a1ed7dfb8af83e615757c0ccfe9f18c6b4ea6771388d9ba8c9"}, + {file = "lxml-6.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:31033dc34636ea6b7d5cc11b1ddbda78a14de858ba9d3e1ed4b69a3085bc521e"}, + {file = "lxml-6.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3893c14c4b6ac5b2d54ba8cf03e99fe5104e592de491f19bd6b82756c09f8004"}, + {file = "lxml-6.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c07da4cebf6889f03ebac8d238f62318e29f495de0aa18a51ea14e61ae907e2e"}, + {file = "lxml-6.1.1-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6f0ce10945fab9c4c06ce14e22af9059d1a87493a9af4501a5b0b9187e21cf2"}, + {file = "lxml-6.1.1-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f8844cd288697c6425c9beba919302241e3278871dc6519515e72b04e987abcf"}, + {file = "lxml-6.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:ed21202aec73cda4d55d1ce57b389aadb90ffb044e6cd1080b8347efe1b1ec84"}, + {file = "lxml-6.1.1.tar.gz", hash = "sha256:ba96ae44888e0185281e937633a743ea90d5a196c6000f82565ebb0580012d40"}, ] [package.extras] @@ -2249,67 +2249,68 @@ files = [ [[package]] name = "matplotlib" -version = "3.10.8" +version = "3.10.9" description = "Python plotting package" optional = false python-versions = ">=3.10" groups = ["main", "dev", "docs"] +markers = "python_version == \"3.10\"" files = [ - {file = "matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7"}, - {file = "matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656"}, - {file = "matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df"}, - {file = "matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17"}, - {file = "matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933"}, - {file = "matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a"}, - {file = "matplotlib-3.10.8-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6be43b667360fef5c754dda5d25a32e6307a03c204f3c0fc5468b78fa87b4160"}, - {file = "matplotlib-3.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2b336e2d91a3d7006864e0990c83b216fcdca64b5a6484912902cef87313d78"}, - {file = "matplotlib-3.10.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efb30e3baaea72ce5928e32bab719ab4770099079d66726a62b11b1ef7273be4"}, - {file = "matplotlib-3.10.8-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d56a1efd5bfd61486c8bc968fa18734464556f0fb8e51690f4ac25d85cbbbbc2"}, - {file = "matplotlib-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238b7ce5717600615c895050239ec955d91f321c209dd110db988500558e70d6"}, - {file = "matplotlib-3.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:18821ace09c763ec93aef5eeff087ee493a24051936d7b9ebcad9662f66501f9"}, - {file = "matplotlib-3.10.8-cp311-cp311-win_arm64.whl", hash = "sha256:bab485bcf8b1c7d2060b4fcb6fc368a9e6f4cd754c9c2fea281f4be21df394a2"}, - {file = "matplotlib-3.10.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:64fcc24778ca0404ce0cb7b6b77ae1f4c7231cdd60e6778f999ee05cbd581b9a"}, - {file = "matplotlib-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9a5ca4ac220a0cdd1ba6bcba3608547117d30468fefce49bb26f55c1a3d5c58"}, - {file = "matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04"}, - {file = "matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f"}, - {file = "matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466"}, - {file = "matplotlib-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:dd80ecb295460a5d9d260df63c43f4afbdd832d725a531f008dad1664f458adf"}, - {file = "matplotlib-3.10.8-cp312-cp312-win_arm64.whl", hash = "sha256:3c624e43ed56313651bc18a47f838b60d7b8032ed348911c54906b130b20071b"}, - {file = "matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6"}, - {file = "matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1"}, - {file = "matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486"}, - {file = "matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce"}, - {file = "matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6"}, - {file = "matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149"}, - {file = "matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645"}, - {file = "matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077"}, - {file = "matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22"}, - {file = "matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39"}, - {file = "matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565"}, - {file = "matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a"}, - {file = "matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958"}, - {file = "matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5"}, - {file = "matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f"}, - {file = "matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b"}, - {file = "matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d"}, - {file = "matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008"}, - {file = "matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c"}, - {file = "matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11"}, - {file = "matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8"}, - {file = "matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50"}, - {file = "matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908"}, - {file = "matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a"}, - {file = "matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1"}, - {file = "matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c"}, - {file = "matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b"}, - {file = "matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f"}, - {file = "matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8"}, - {file = "matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7"}, - {file = "matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3"}, - {file = "matplotlib-3.10.8-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6da7c2ce169267d0d066adcf63758f0604aa6c3eebf67458930f9d9b79ad1db1"}, - {file = "matplotlib-3.10.8-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9153c3292705be9f9c64498a8872118540c3f4123d1a1c840172edf262c8be4a"}, - {file = "matplotlib-3.10.8-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ae029229a57cd1e8fe542485f27e7ca7b23aa9e8944ddb4985d0bc444f1eca2"}, - {file = "matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3"}, + {file = "matplotlib-3.10.9-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77210dce9cb8153dffc967efaae990543392563d5a376d4dd8539bebcb0ed217"}, + {file = "matplotlib-3.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1e7698ac9868428e84d2c967424803b2472ff7167d9d6590d4204ed775343c3b"}, + {file = "matplotlib-3.10.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1aa972116abb4c9d201bf245620b433726cb6856f3bef6a78f776a00f5c92d37"}, + {file = "matplotlib-3.10.9-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae2f11957b27ce53497dd4d7b235c4d4f1faf383dfb39d0c5beb833bff883294"}, + {file = "matplotlib-3.10.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b049278ddce116aaa1c1377ebf58adea909132dfce0281cf7e3a1ea9fc2e2c65"}, + {file = "matplotlib-3.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:82834c3c292d24d3a8aae77cd2d20019de69d692a34a970e4fdb8d33e2ea3dda"}, + {file = "matplotlib-3.10.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:68cfdcede415f7c8f5577b03303dd94526cdb6d11036cecdc205e08733b2d2bb"}, + {file = "matplotlib-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfca0129678bd56379db26c52b5d77ed7de314c047492fbdc763aa7501710cfb"}, + {file = "matplotlib-3.10.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e436d155fa8a3399dc62683f8f5d0e2e50d25d0144a73edd73f82eec8f4abfb"}, + {file = "matplotlib-3.10.9-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56fc0bd271b00025c6edfdc7c2dcd247372c8e1544971d62e1dc7c17367e8bf9"}, + {file = "matplotlib-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5a6104ed666402ba5106d7f36e0e0cdca4e8d7fa4d39708ca88019e2835a2eb"}, + {file = "matplotlib-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:d730e984eddf56974c3e72b6129c7ca462ac38dc624338f4b0b23eb23ecba00f"}, + {file = "matplotlib-3.10.9-cp311-cp311-win_arm64.whl", hash = "sha256:51bf0ddbdc598e060d46c16b5590708f81a1624cefbaaf62f6a81bf9285b8c80"}, + {file = "matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1"}, + {file = "matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320"}, + {file = "matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285"}, + {file = "matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2"}, + {file = "matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf"}, + {file = "matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6"}, + {file = "matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42"}, + {file = "matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f"}, + {file = "matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e"}, + {file = "matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f"}, + {file = "matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838"}, + {file = "matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2"}, + {file = "matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921"}, + {file = "matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8"}, + {file = "matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9"}, + {file = "matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4"}, + {file = "matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc"}, + {file = "matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99"}, + {file = "matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d"}, + {file = "matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8"}, + {file = "matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38"}, + {file = "matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d"}, + {file = "matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f"}, + {file = "matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b"}, + {file = "matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2"}, + {file = "matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716"}, + {file = "matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f"}, + {file = "matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456"}, + {file = "matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe"}, + {file = "matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6"}, + {file = "matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c"}, + {file = "matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4"}, + {file = "matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf"}, + {file = "matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39"}, + {file = "matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c"}, + {file = "matplotlib-3.10.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1872fb212a05b729e649754a72d5da61d03e0554d76e80303b6f83d1d2c0552b"}, + {file = "matplotlib-3.10.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:985f2238880e2e69093f588f5fe2e46771747febf0649f3cf7f7b7480875317f"}, + {file = "matplotlib-3.10.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6640f75af2c6148293caa0a2b39dd806a492dd66c8a8b04035813e33d0fd2585"}, + {file = "matplotlib-3.10.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:42fb814efabe95c06c1994d8ab5a8385f43a249e23badd3ba931d4308e5bca20"}, + {file = "matplotlib-3.10.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f76e640a5268850bfda54b5131b1b1941cc685e42c5fa98ed9f2d64038308cba"}, + {file = "matplotlib-3.10.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3fc0364dfbe1d07f6d15c5ebd0c5bf89e126916e5a8667dd4a7a6e84c36653d4"}, + {file = "matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358"}, ] [package.dependencies] @@ -2324,7 +2325,75 @@ pyparsing = ">=3" python-dateutil = ">=2.7" [package.extras] -dev = ["meson-python (>=0.13.1,<0.17.0)", "pybind11 (>=2.13.2,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7)"] +dev = ["meson-python (>=0.13.1,<0.17.0)", "pybind11 (>=2.13.2,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7,<10)"] + +[[package]] +name = "matplotlib" +version = "3.11.0" +description = "Python plotting package" +optional = false +python-versions = ">=3.11" +groups = ["main", "dev", "docs"] +markers = "python_version >= \"3.11\"" +files = [ + {file = "matplotlib-3.11.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f857524b442f0f36e641868ce2171aafa88cb0bc0644f4e1d8a5df9b32649fef"}, + {file = "matplotlib-3.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:57baa92fdc82948ed716eae6d2579d4d6f40965cd8d2f416755b4a72580a3233"}, + {file = "matplotlib-3.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:630eee0e67d35cce2019a0e670719f4816e3b86aff0fa72729f6c69786fceb45"}, + {file = "matplotlib-3.11.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5106c444d0bf966eee2853548c03772af4ab7199118e086c62fbac8ccb07c055"}, + {file = "matplotlib-3.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d7aea652b58e686444079be3376ef546bffa1eee9b9bb9c472b9fcf6cf410d3"}, + {file = "matplotlib-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:70a5b3e9a5dab708c0f039709ae7c68d5b4d254e291ef76492cdba230c8bb5e4"}, + {file = "matplotlib-3.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:3d68266213e73823ac3be90615bab0cf31f88851e114cdb1dd25dacf3b01e1a7"}, + {file = "matplotlib-3.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06b5872e9cf11adc8f589ded3ce11bc3e1061ad498259664fabc1f6615beb918"}, + {file = "matplotlib-3.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0515d495124be3124340e59f164d901ed4484e2246a5b74cfa483cac3b80bd97"}, + {file = "matplotlib-3.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be5f93a1d21981bfb802ded0d77a0caa92d4342a47d45754fac77e314a506344"}, + {file = "matplotlib-3.11.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41635d7909d19e52e924a521dde6d8f670b0f53ab1d0e8c331fa831554f681d1"}, + {file = "matplotlib-3.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:94f5000f67ca9faa300863ea17f8bce9175cb67b88bec4bc7780502d53dd7c9e"}, + {file = "matplotlib-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ac6f1ef39f3d0f9e2463303013094992cdbe0f85f43bc54155bc472b2042768e"}, + {file = "matplotlib-3.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:9dd11fb612ce7bc60b1de5b4fc87ff959d22317b5de42aabf392f66f97af22eb"}, + {file = "matplotlib-3.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce3b839b34ae1f430b4616893a2945a2999debaa7e94e7e29a2a8bbf286f7b5"}, + {file = "matplotlib-3.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:373db8f91214e8ccaf35ac833cc1dd59dd961e148bbd55dd027141591dde1313"}, + {file = "matplotlib-3.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be152b7570324dc8d01574cc9474dd2d803237acf528bcbb5b211fa347461a09"}, + {file = "matplotlib-3.11.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:126f256df600652d7e4b394cf3164ff75210a00038f287c95a012a6f58d0e83f"}, + {file = "matplotlib-3.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:03acfeddf87b0dddb11b081ef7740ad445a3ca8bcb6b8e3011b08f2cf802b75c"}, + {file = "matplotlib-3.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:ab3722f04f3ff34c23b5012c5873d2894174e06c3822fcdac3610965a5ac7d06"}, + {file = "matplotlib-3.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:c945824670fb8915b4ac879e5e61f3c58e0913022f70a0de4c082b17372f8771"}, + {file = "matplotlib-3.11.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3489c3dc487669b4a980bc3068f87856de7a1564248d3f6c629efb2a58b03f24"}, + {file = "matplotlib-3.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6a98f5476ce784a50ce09998f4ae1e6a9f25043cef8a480c98949902eda74620"}, + {file = "matplotlib-3.11.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:565af866fd63e4bd3f987d580afe27c44c2552a3b3305f4ecbb85133601ea6f3"}, + {file = "matplotlib-3.11.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6b3e64dea5062c570f04358e2711859f3531b459f29516274fbad889079e4f3"}, + {file = "matplotlib-3.11.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:942b37c5db1899610bd1543ce8e13e4ecff9a4633e7f63bb6aa9205d2644ebd1"}, + {file = "matplotlib-3.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c08e649a6313e1291e713623b97a38e5bb4aa580b2a100a94a3309bc6b9c8eb3"}, + {file = "matplotlib-3.11.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2746cd2c113742ff6ce37a864c5ac5fd7aa644568f445e66166e457ac78e40e0"}, + {file = "matplotlib-3.11.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3338e3e3de128cf50d0d2fb92a122815daf9c755bd882a474343c05f8fd7ec79"}, + {file = "matplotlib-3.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:25c2e5455efd8d99f41fb79871a31feb7d301569642e332ec58d72cfe9282bc3"}, + {file = "matplotlib-3.11.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9695457a467ff86d23f35037a43deb6f1134dd6d3e2ac8ce1e2087cff09ffb9"}, + {file = "matplotlib-3.11.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19c16c61dea63b3582918503e6b294193961261d9daa806d4ae2151f1ad05430"}, + {file = "matplotlib-3.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2d72ea8b7924f3cb955e61518d21e43b3df1e6c8a793b480a0c1214f185d30ba"}, + {file = "matplotlib-3.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:1c02da0a629dfa9debf52725ea06866b74c1fb70a895bae05e4493d34074f9f2"}, + {file = "matplotlib-3.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aa55d73b3117d4b07f959cd9eb6f69b375d8df3414139c479388e551aa5d999d"}, + {file = "matplotlib-3.11.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a9d8c6e7cd2f0ddf11d8d92e520dd1d9d2abb0cf6ac8831e338666c81e905847"}, + {file = "matplotlib-3.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:be050fcf32f729eda99f7f75a80bf67612ce16ab9ac1c23a387dcaede95cb70e"}, + {file = "matplotlib-3.11.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dfabef0230d0697aa0d717385194dd41162e00207a68bf4abf94c2bf4c27dca0"}, + {file = "matplotlib-3.11.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1644db30e759199443493ac5e5caec24fdb775a8f6123021f85ba47c4133c3cb"}, + {file = "matplotlib-3.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:15b0d160079cb10699a0e98b5989c70677b2df7cacdc62af67c30f2facec46d9"}, + {file = "matplotlib-3.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:446307e6b04b57b1f1239e228a1ec2af0d589a1008cebc3dfa3f5441d095cfb6"}, + {file = "matplotlib-3.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:652fb5696271d4c50f196d22a5ff4f8e4444c74f847423570d7dc0aa2bbd0159"}, + {file = "matplotlib-3.11.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:81ae77077a1e16d37a5b61096ccb07c8d90a99b518fa8256b8f21578932f2f62"}, + {file = "matplotlib-3.11.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ddef37840695f5eef65f9f070fe2d2f510f584c2156203f9f622a5b0584efffd"}, + {file = "matplotlib-3.11.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf662e5ac5707658cb931e19972c4bd99f7b4f8b7bf79d3c821d239fa6b71e64"}, + {file = "matplotlib-3.11.0.tar.gz", hash = "sha256:68c0c7be01b30dcca3638934f7f591df73401235cbdbf0d1ab1c71e7db7f8b57"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +kiwisolver = ">=1.3.1" +numpy = ">=1.25" +packaging = ">=20.0" +pillow = ">=9" +pyparsing = ">=3" +python-dateutil = ">=2.7" [[package]] name = "matplotlib-inline" @@ -2375,14 +2444,14 @@ files = [ [[package]] name = "mistune" -version = "3.2.1" +version = "3.3.3" description = "A sane and fast Markdown parser with useful plugins and renderers" optional = false python-versions = ">=3.8" groups = ["dev", "docs"] files = [ - {file = "mistune-3.2.1-py3-none-any.whl", hash = "sha256:78cdb0ba5e938053ccf63651b352508d2efa9411dc8810bfb05f2dc5140c0048"}, - {file = "mistune-3.2.1.tar.gz", hash = "sha256:7c8e5501d38bac1582e067e46c8343f17d57ea1aaa735823f3aba1fd59c88a28"}, + {file = "mistune-3.3.3-py3-none-any.whl", hash = "sha256:99de1585e42dcbd826faa9e11a202727a5e202e4e4722a4c69ac1ff615793dd7"}, + {file = "mistune-3.3.3.tar.gz", hash = "sha256:c4c6c0c840b8637a2e9b8b6d607eb7c8f00888bf14c754409bcd339e848c2477"}, ] [package.dependencies] @@ -2805,14 +2874,14 @@ files = [ [[package]] name = "packaging" -version = "25.0" +version = "26.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["main", "dev", "docs", "test"] files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, + {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"}, + {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"}, ] [[package]] @@ -2873,103 +2942,99 @@ ptyprocess = ">=0.5" [[package]] name = "pillow" -version = "12.2.0" +version = "12.3.0" description = "Python Imaging Library (fork)" optional = false python-versions = ">=3.10" groups = ["main", "dev", "docs"] files = [ - {file = "pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f"}, - {file = "pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97"}, - {file = "pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff"}, - {file = "pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec"}, - {file = "pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136"}, - {file = "pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c"}, - {file = "pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3"}, - {file = "pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa"}, - {file = "pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032"}, - {file = "pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5"}, - {file = "pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024"}, - {file = "pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab"}, - {file = "pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65"}, - {file = "pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7"}, - {file = "pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e"}, - {file = "pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705"}, - {file = "pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176"}, - {file = "pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b"}, - {file = "pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909"}, - {file = "pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808"}, - {file = "pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60"}, - {file = "pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe"}, - {file = "pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5"}, - {file = "pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421"}, - {file = "pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987"}, - {file = "pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76"}, - {file = "pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005"}, - {file = "pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780"}, - {file = "pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5"}, - {file = "pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5"}, - {file = "pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940"}, - {file = "pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5"}, - {file = "pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414"}, - {file = "pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c"}, - {file = "pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2"}, - {file = "pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c"}, - {file = "pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795"}, - {file = "pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f"}, - {file = "pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed"}, - {file = "pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9"}, - {file = "pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed"}, - {file = "pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3"}, - {file = "pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9"}, - {file = "pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795"}, - {file = "pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e"}, - {file = "pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b"}, - {file = "pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06"}, - {file = "pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b"}, - {file = "pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f"}, - {file = "pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612"}, - {file = "pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c"}, - {file = "pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea"}, - {file = "pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4"}, - {file = "pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4"}, - {file = "pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea"}, - {file = "pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24"}, - {file = "pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98"}, - {file = "pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453"}, - {file = "pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8"}, - {file = "pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b"}, - {file = "pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295"}, - {file = "pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed"}, - {file = "pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae"}, - {file = "pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601"}, - {file = "pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be"}, - {file = "pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f"}, - {file = "pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286"}, - {file = "pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50"}, - {file = "pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104"}, - {file = "pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7"}, - {file = "pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150"}, - {file = "pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1"}, - {file = "pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463"}, - {file = "pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3"}, - {file = "pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166"}, - {file = "pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe"}, - {file = "pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd"}, - {file = "pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e"}, - {file = "pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06"}, - {file = "pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43"}, - {file = "pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354"}, - {file = "pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1"}, - {file = "pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1"}, - {file = "pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e"}, - {file = "pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5"}, + {file = "pillow-12.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6c0016e7b354317c4e9e525b937ac8596c38d2d232b419529b9cd7a1cd46e39a"}, + {file = "pillow-12.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bcc33feacfaefce60c12fd500a277533bdc02b10a19f7f6d348763d8140bbba7"}, + {file = "pillow-12.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5594fc43d548a7ed94949d139aa1341b270f1863f11cfd37f5a6c8b778a6b67f"}, + {file = "pillow-12.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0606c8bf2cdefea14a43530f7657cbbb7ecf1c4222512492ef4a4434a9501ec"}, + {file = "pillow-12.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:85f998ea1848bc6757289e739cfbdda3a04adfd58b02fc018ce54d754a5ce468"}, + {file = "pillow-12.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:25b9b82bb22e6e2b3cd07b39c68b7b862001226cb3dff7130d1cb914121b39ed"}, + {file = "pillow-12.3.0-cp310-cp310-win32.whl", hash = "sha256:37dc8f7bbb66efe481bb60defacef820c950c24713fb44962ed6aa2a50966de1"}, + {file = "pillow-12.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:300557495eb45ebb8aec96c2da9c4be642fbf7cd937278b4013ba894ea8eb0eb"}, + {file = "pillow-12.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:514435a37670e3e5e08f3945b68718b6ed329bb84367777e16f9f4dfe1e61a0f"}, + {file = "pillow-12.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756"}, + {file = "pillow-12.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6"}, + {file = "pillow-12.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd"}, + {file = "pillow-12.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd"}, + {file = "pillow-12.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4f883547d4b7f0495ebe7056b0cc2aea76094e7a4abc8e933540f3271df27d9c"}, + {file = "pillow-12.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:236ff70b9312fb68943c703aa842ca6a758abfa45ac187a5e7c1452e96ef72b5"}, + {file = "pillow-12.3.0-cp311-cp311-win32.whl", hash = "sha256:10e41f0fbf1eec8cfd234b8fe17a4caac7c9d0db4c204d3c173a8f9f6ef3232b"}, + {file = "pillow-12.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8e95e1385e4998ae9694eeaa4730ba5457ff61185b3a55e2e7bea0880aef452a"}, + {file = "pillow-12.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:ebaea975e03d3141d9d3a507df75c9b3ec90fa9d2ffd07567b3a978d9d790b26"}, + {file = "pillow-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965"}, + {file = "pillow-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7"}, + {file = "pillow-12.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9"}, + {file = "pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91"}, + {file = "pillow-12.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c"}, + {file = "pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df"}, + {file = "pillow-12.3.0-cp312-cp312-win32.whl", hash = "sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f"}, + {file = "pillow-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09"}, + {file = "pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510"}, + {file = "pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89"}, + {file = "pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace"}, + {file = "pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec"}, + {file = "pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66"}, + {file = "pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35"}, + {file = "pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65"}, + {file = "pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3"}, + {file = "pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a"}, + {file = "pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e"}, + {file = "pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f"}, + {file = "pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8"}, + {file = "pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b"}, + {file = "pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330"}, + {file = "pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217"}, + {file = "pillow-12.3.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930"}, + {file = "pillow-12.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8"}, + {file = "pillow-12.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0"}, + {file = "pillow-12.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321"}, + {file = "pillow-12.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b"}, + {file = "pillow-12.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198"}, + {file = "pillow-12.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130"}, + {file = "pillow-12.3.0-cp314-cp314-win32.whl", hash = "sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a"}, + {file = "pillow-12.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d"}, + {file = "pillow-12.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838"}, + {file = "pillow-12.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e"}, + {file = "pillow-12.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17"}, + {file = "pillow-12.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385"}, + {file = "pillow-12.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c"}, + {file = "pillow-12.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d"}, + {file = "pillow-12.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931"}, + {file = "pillow-12.3.0-cp314-cp314t-win32.whl", hash = "sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7"}, + {file = "pillow-12.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c"}, + {file = "pillow-12.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45"}, + {file = "pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139"}, + {file = "pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402"}, + {file = "pillow-12.3.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c"}, + {file = "pillow-12.3.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f"}, + {file = "pillow-12.3.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701"}, + {file = "pillow-12.3.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace"}, + {file = "pillow-12.3.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4"}, + {file = "pillow-12.3.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39"}, + {file = "pillow-12.3.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71"}, + {file = "pillow-12.3.0-cp315-cp315-win32.whl", hash = "sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827"}, + {file = "pillow-12.3.0-cp315-cp315-win_amd64.whl", hash = "sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5"}, + {file = "pillow-12.3.0-cp315-cp315-win_arm64.whl", hash = "sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658"}, + {file = "pillow-12.3.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf"}, + {file = "pillow-12.3.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64"}, + {file = "pillow-12.3.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e"}, + {file = "pillow-12.3.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777"}, + {file = "pillow-12.3.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1"}, + {file = "pillow-12.3.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9"}, + {file = "pillow-12.3.0-cp315-cp315t-win32.whl", hash = "sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8"}, + {file = "pillow-12.3.0-cp315-cp315t-win_amd64.whl", hash = "sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418"}, + {file = "pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59"}, + {file = "pillow-12.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468"}, + {file = "pillow-12.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94"}, + {file = "pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e"}, + {file = "pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7e480451b9fa137494bccd3a7d69adbe8ac65a87d97be61e11f1b1050a5bac3"}, + {file = "pillow-12.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:04f01d28a6aaff387bf842a13be313df23ba0597a44f1a976c9feb3c6ff4711a"}, + {file = "pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce"}, ] [package.extras] @@ -2977,7 +3042,7 @@ docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-autobuild", "sphinx-copybut fpx = ["olefile"] mic = ["olefile"] test-arrow = ["arro3-compute", "arro3-core", "nanoarrow", "pyarrow"] -tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma (>=5)", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"] +tests = ["coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "setuptools", "trove-classifiers (>=2024.10.12)"] xmp = ["defusedxml"] [[package]] @@ -4004,14 +4069,14 @@ files = [ [[package]] name = "soupsieve" -version = "2.7" +version = "2.8.4" description = "A modern CSS selector implementation for Beautiful Soup." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main", "dev", "docs"] files = [ - {file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}, - {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, + {file = "soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65"}, + {file = "soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e"}, ] [[package]] @@ -4471,25 +4536,24 @@ files = [ [[package]] name = "tqdm" -version = "4.67.3" +version = "4.68.4" description = "Fast, Extensible Progress Meter" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf"}, - {file = "tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb"}, + {file = "tqdm-4.68.4-py3-none-any.whl", hash = "sha256:5168118b2368f48c561afda8020fd79195b1bdb0bdf8086b88442c267a315dc2"}, + {file = "tqdm-4.68.4.tar.gz", hash = "sha256:19829c9673638f2a0b8617da4cdcb927e831cd88bcfcb6e78d42a4d1af131520"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] -dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] -discord = ["requests"] +discord = ["envwrap", "requests"] notebook = ["ipywidgets (>=6)"] -slack = ["slack-sdk"] -telegram = ["requests"] +slack = ["envwrap", "slack-sdk"] +telegram = ["envwrap", "requests"] [[package]] name = "traitlets" @@ -4521,14 +4585,14 @@ files = [ [[package]] name = "typing-extensions" -version = "4.14.0" +version = "4.16.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" groups = ["main", "dev", "docs", "test"] files = [ - {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, - {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, + {file = "typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8"}, + {file = "typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5"}, ] [[package]] @@ -4652,4 +4716,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.15" -content-hash = "c0b084d2a2bfd7cc46d1650fe467a23f41e5a867fec94950a2c03d6515d52139" +content-hash = "79c958fc36902b19c8dc961de5168fc6273168bc2f026bc1a511101367a50223" diff --git a/pyproject.toml b/pyproject.toml index 50b6899a2..f3083d3fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,17 +22,17 @@ dependencies = [ "scipy (>=1.17.0,<1.18.0) ; python_version >= '3.11'", "astropy (>=6.1.7,<8.0.0)", - "matplotlib (>=3.10.5,<4.0.0)", - "pooch (>=1.8.2,<2.0.0)", + "matplotlib (>=3.10.8,<4.0.0)", + "pooch (>=1.9.0,<2.0.0)", "docutils (>=0.19,<1.0)", "httpxyz (>=0.31.2,<1.0)", - "beautifulsoup4 (>=4.13.3,<5.0.0)", - "lxml (>=6.0.2,<7.0.0)", + "beautifulsoup4 (>=4.15.0,<5.0.0)", + "lxml (>=6.1.1,<7.0.0)", "pyyaml (>=6.0.3,<7.0.0)", "more-itertools (>=10.2.0,<11.0.0)", - "tqdm (>=4.66.6,<5.0.0)", - "packaging (>=25.0,<27.0)", + "tqdm (>=4.67.4,<5.0.0)", + "packaging (>=26.0,<28.0)", "synphot (>=1.6.1,<2.0.0)", "skycalc-ipy (>=0.7.0,<1.0)", From 7a851ab944d0990bc46e848f3ae3397a52b3a494 Mon Sep 17 00:00:00 2001 From: "astarvienna-bot[bot]" <217837468+myciapp[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 09:11:09 +0000 Subject: [PATCH 13/68] Bumping version from 0.11.5a5 to 0.11.5a6 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f3083d3fa..b01a6bb2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ScopeSim" -version = "0.11.5a5" +version = "0.11.5a6" description = "Generalised telescope observation simulator" license = {text = "GPL-3.0-or-later"} authors = [ From 46d1c1bbeed483b654e49abc8062f0b9a71d6423 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Mon, 20 Jul 2026 13:01:55 +0200 Subject: [PATCH 14/68] Fix float edge case in coords --- scopesim/optics/image_plane_utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scopesim/optics/image_plane_utils.py b/scopesim/optics/image_plane_utils.py index 6de378b6c..4f713acad 100644 --- a/scopesim/optics/image_plane_utils.py +++ b/scopesim/optics/image_plane_utils.py @@ -434,7 +434,12 @@ def overlay_image(small_im, big_im, coords, mask=None, sub_pixel=False): coords = np.array([*coords, (big_im.shape[0] - 1) / 2]) # FIXME: this would not be necessary if we used WCS instead of manual 2pix - coords = np.ceil(np.asarray(coords).round(10)).astype(int) + # Round to 1e-4 pix before ceil: WCS deg-space round-trips leave + # float dust of order 1e-8..1e-6 pix on integer-valued coords, which + # ceil would amplify to a full-pixel shift; genuine sub-pixel intent + # cannot be finer than 0.5 pix here (sub_pixel is not implemented), + # and the half-integer even-shape convention is preserved exactly. + coords = np.ceil(np.asarray(coords).round(4)).astype(np.intp) idx = coords.astype(int)[::-1] - np.array(small_im.shape) // 2 # Image ranges From d104780921a624c208b341f2ae1e3251aa07daba Mon Sep 17 00:00:00 2001 From: teutoburg Date: Mon, 20 Jul 2026 14:10:11 +0200 Subject: [PATCH 15/68] Fix aliasing bug in image overlay --- scopesim/optics/image_plane_utils.py | 61 +++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/scopesim/optics/image_plane_utils.py b/scopesim/optics/image_plane_utils.py index 4f713acad..b4901e70b 100644 --- a/scopesim/optics/image_plane_utils.py +++ b/scopesim/optics/image_plane_utils.py @@ -539,13 +539,62 @@ def rescale_imagehdu(imagehdu: fits.ImageHDU, pixel_scale: float | u.Quantity, sum_orig = np.sum(imagehdu.data) # Perform the rescaling. Axes need to be inverted because python. - new_im = ndi.zoom(imagehdu.data, zoom[::-1], order=spline_order) + zoom_np = zoom[::-1] + data = imagehdu.data + in_shape = np.array(data.shape) # pre-zoom, for the realised-zoom CRPIX + + # ndi.zoom point-samples the input on the output lattice; for zoom < 1 that + # DECIMATES rather than integrates, so a source narrower than the sample + # spacing can be aliased away partially or (on an unlucky grid phase) + # entirely -- silently, since the conserve_flux guard below skips a zeroed + # array. Downsample each such axis with an exact area-weighted rebin (the + # cumulative sum evaluated at the output bin edges) instead. Unlike a + # smoothing prefilter this integrates without SPREADING flux beyond the + # output cell, so a compact source cannot leak into wings that a smaller + # downstream FOV would clip (which would defeat conserve_flux). It conserves + # flux to machine precision by construction and handles non-integer ratios. + # + # Gated on spline_order > 0: order == 0 is nearest-neighbour resampling of + # categorical data (masks, strehl-region label maps), where integrating / + # averaging is meaningless and would synthesise phantom label values -- those + # keep the stock ndi.zoom path. Upsampled axes also use stock ndi.zoom. + if spline_order > 0 and (zoom_np < 1).any(): + out = np.asarray(data, dtype=float) + for _ax in range(out.ndim): + _n_in = out.shape[_ax] + _n_out = max(int(round(_n_in * zoom_np[_ax])), 1) + if _n_out == _n_in: + continue + if _n_out < _n_in: + _a = np.moveaxis(out, _ax, 0) + _csum = np.empty((_n_in + 1,) + _a.shape[1:], dtype=float) + _csum[0] = 0.0 + np.cumsum(_a, axis=0, out=_csum[1:]) + _edges = np.linspace(0.0, _n_in, _n_out + 1) + _lo = np.floor(_edges).astype(np.intp) + _frac = _edges - _lo + _loc = np.clip(_lo, 0, _n_in) + _hic = np.clip(_lo + 1, 0, _n_in) + _fr = _frac[(...,) + (None,) * (_a.ndim - 1)] + _vals = _csum[_loc] + (_csum[_hic] - _csum[_loc]) * _fr + out = np.moveaxis(np.diff(_vals, axis=0), 0, _ax) + else: + _zf = np.ones(out.ndim) + _zf[_ax] = _n_out / _n_in + out = ndi.zoom(out, _zf, order=spline_order, mode="nearest") + new_im = out + else: + new_im = ndi.zoom(data, zoom_np, order=spline_order) if conserve_flux: new_im = np.nan_to_num(new_im, copy=False) sum_new = np.sum(new_im) if sum_new != 0: new_im *= sum_orig / sum_new + elif sum_orig != 0: + logger.warning( + "rescale_imagehdu: all input flux (%g) was lost in resampling; " + "flux cannot be conserved.", sum_orig) imagehdu.data = new_im @@ -577,8 +626,16 @@ def rescale_imagehdu(imagehdu: fits.ImageHDU, pixel_scale: float | u.Quantity, # # The transformation only applies to spatial coordinates, which we # assume to be the first two in the WCS. + # CDELT keeps the nominal zoom (CDELT' == pixel_scale exactly). CRPIX + # uses the realised zoom n_out/n_in (ndi.zoom / the rebin both produce + # round(n_in*zoom) pixels), else the reference pixel -- and every source + # -- is misplaced by up to half an output pixel when n_in*zoom is not + # integer. + n_in_sp = in_shape[::-1][:2].astype(float) + n_out_sp = np.array(imagehdu.data.shape[::-1][:2], dtype=float) + zoom_real = n_out_sp / n_in_sp ww.wcs.cdelt[:2] /= zoom[:2] - ww.wcs.crpix[:2] = 0.5 + (ww.wcs.crpix[:2] - 0.5) * zoom[:2] + ww.wcs.crpix[:2] = 0.5 + (ww.wcs.crpix[:2] - 0.5) * zoom_real logger.debug("new crpix %s", ww.wcs.crpix) imagehdu.header.update(ww.to_header()) From e228a3a4b0da331475d9c0d8381a256555104675 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Mon, 20 Jul 2026 15:28:18 +0200 Subject: [PATCH 16/68] Bumping version from 0.11.5a6 to 0.12.0b0.dev0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b01a6bb2b..dd160cd6e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ScopeSim" -version = "0.11.5a6" +version = "0.12.0b0.dev0" description = "Generalised telescope observation simulator" license = {text = "GPL-3.0-or-later"} authors = [ From ca8f792c5f5efd465791bfe63044aea5029871ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 15:43:32 +0000 Subject: [PATCH 17/68] Bump actions/setup-python from 6 to 7 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6 to 7. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/notebooks_dispatch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/notebooks_dispatch.yml b/.github/workflows/notebooks_dispatch.yml index 429739e31..1fac5689e 100644 --- a/.github/workflows/notebooks_dispatch.yml +++ b/.github/workflows/notebooks_dispatch.yml @@ -36,7 +36,7 @@ jobs: run: pipx install poetry==2.1.4 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@v7 with: python-version: ${{ matrix.python-version }} From 2af1bb58bb2e8951f78c36abcf7ce623b1b1cb89 Mon Sep 17 00:00:00 2001 From: oczoske Date: Mon, 20 Jul 2026 19:04:44 +0200 Subject: [PATCH 18/68] Use optimal inverse transform for rectification --- scopesim/effects/metis_lms_trace_list.py | 12 ++++- scopesim/effects/spectral_trace_list_utils.py | 44 ++++++++++++++++++- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/scopesim/effects/metis_lms_trace_list.py b/scopesim/effects/metis_lms_trace_list.py index bf8c0d88f..76ba9b298 100644 --- a/scopesim/effects/metis_lms_trace_list.py +++ b/scopesim/effects/metis_lms_trace_list.py @@ -38,6 +38,7 @@ class MetisLMSSpectralTraceList(SpectralTraceList): "pixscale": 0.0082, # arcsec "grat_spacing": 18.2, "fp2_platescale": 0.303, + "fit_inverse": True, } def __init__(self, **kwargs): @@ -177,6 +178,10 @@ def rectify_cube(self, hdulist, xi_min=None, xi_max=None, interps=None, If provided, there must be one for each image extension in `hdulist`. The functions go from pixels to the images and can be created with, e.g., RectBivariateSpline. + fit_inverse : bool [default True] + By default the method fits the inverse transform to be the optimal + inverse of the forward transform. Set `fit_inverse=False` to use the + predefined inverse matrix transform. """ try: inhdul = fits.open(hdulist) @@ -186,8 +191,12 @@ def rectify_cube(self, hdulist, xi_min=None, xi_max=None, interps=None, # Create interpolation functions if interps is None: logger.info("Computing interpolation functions") + # TODO: Allow cubic interpolation? interps = make_image_interpolations(inhdul, kx=1, ky=1) + # Use fitted inverse or the matrices? + fit_inverse = kwargs.get("fit_inverse", True) + # Create a common wcs for the rectification dwave = from_currsys("!SIM.spectral.spectral_bin_width", self.cmds) xi_min = np.min(self.slicelist["left"]) @@ -217,7 +226,8 @@ def rectify_cube(self, hdulist, xi_min=None, xi_max=None, interps=None, result = spt.rectify(hdulist, interps=interps, wave_min=wave_min, wave_max=wave_max, xi_min=xi_min, xi_max=xi_max, - bin_width=dwave) + bin_width=dwave, + fit_inverse=fit_inverse) cube[:, i, :] = result.data.T # FIXME: use wcs object here diff --git a/scopesim/effects/spectral_trace_list_utils.py b/scopesim/effects/spectral_trace_list_utils.py index 7541f9cbf..c67c1d1ad 100644 --- a/scopesim/effects/spectral_trace_list_utils.py +++ b/scopesim/effects/spectral_trace_list_utils.py @@ -87,6 +87,7 @@ def __init__(self, trace_tbl, cmds=None, **kwargs): # Declaration of other attributes self._xilamimg = None self.dlam_per_pix = None + self.fit_inverse = True @property def trace_id(self): @@ -322,6 +323,7 @@ def rectify(self, hdulist, interps=None, wcs=None, **kwargs): Spatial limits of the slit on the sky. This should be taken from the header of the hdulist, but this is not yet provided by scopesim """ + self.fit_inverse = kwargs.get("fit_inverse", True) wave_min = kwargs.get("wave_min", self.wave_min) wave_max = kwargs.get("wave_max", @@ -386,9 +388,20 @@ def rectify(self, hdulist, interps=None, wcs=None, **kwargs): # Make sure that we do have microns Lam = Lam * u.Unit(wcs.wcs.cunit[0]).to(u.um) + ## TODO: Replace self.xilam2x and self.xilam2y by fitted + ## functions fit_xilam2x and fit_xilam2y, which need + ## to be determined by mapping a grid of (xi, lam) to (x,y) + ## and back to (xip, lamp); then fit (x,y)(xip, lamp). + if self.fit_inverse: + fit_xilam2x, fit_xilam2y = self._fit_transform(xi_min, xi_max, + wave_min, wave_max) + else: + logger.debug("Using matrix transform") + fit_xilam2x, fit_xilam2y = self.xilam2x, self.xilam2y + # Convert Xi, Lam to focal plane units - Xarr = self.xilam2x(Xi, Lam) - Yarr = self.xilam2y(Xi, Lam) + Xarr = fit_xilam2x(Xi, Lam) + Yarr = fit_xilam2y(Xi, Lam) rect_spec = np.zeros_like(Xarr, dtype=np.float32) @@ -413,6 +426,33 @@ def rectify(self, hdulist, interps=None, wcs=None, **kwargs): header["EXTNAME"] = self.trace_id return fits.ImageHDU(data=rect_spec, header=header) + def _fit_transform(self, ximin, ximax, wmin, wmax): + """Fit inverses to self.xy2xi and self.xy2lam + + The method replaces self.xilam2x and self.xilam2y by fitted + functions fit_xilam2x and fit_xilam2y, determined by mapping a grid of + (xi, lam) to (x,y) and back to (xip, lamp); then fit (x,y)(xip, lamp). + """ + + # Build input (xi, lam) grid + xi = np.linspace(ximin, ximax, 51) + lam = np.linspace(wmin, wmax, 201) + + # Do a full round using the matrix transforms + x2d = self.xilam2x(xi, lam, grid=True) + y2d = self.xilam2y(xi, lam, grid=True) + xip = self.xy2xi(x2d, y2d, grid=False) + lamp = self.xy2lam(x2d, y2d, grid=False) + + # Determine the fits + pinit_x = Polynomial2D(degree=4) + pinit_y = Polynomial2D(degree=4) + fitter = fitting.LinearLSQFitter() + xilam2x = fitter(pinit_x, xip, lamp, x2d) + xilam2y = fitter(pinit_y, xip, lamp, y2d) + + return xilam2x, xilam2y + def footprint(self, wave_min=None, wave_max=None, xi_min=None, xi_max=None): """ Return corners of rectangle enclosing spectral trace. From c46844b2339fae6f2ac4cedbd58ffb564d9b86cd Mon Sep 17 00:00:00 2001 From: oczoske Date: Wed, 22 Jul 2026 21:03:27 +0200 Subject: [PATCH 19/68] Remove reflectance factor from integrating sphere background --- scopesim/effects/metis_wcu/metis_wcu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scopesim/effects/metis_wcu/metis_wcu.py b/scopesim/effects/metis_wcu/metis_wcu.py index 02086fa57..04e589af9 100644 --- a/scopesim/effects/metis_wcu/metis_wcu.py +++ b/scopesim/effects/metis_wcu/metis_wcu.py @@ -402,7 +402,7 @@ def compute_lamp_emission(self): # background emission from integrating sphere self.is_bg = BlackBody(self.is_temp, scale=self.bb_scale) - self.intens_bg = self.rho_is(lam) * self.is_bg(lam) + self.intens_bg = self.is_bg(lam) self.intensity = self.intens_lamp + self.intens_bg From d180ec5ce4f2f4f3079107b9118958c8182ffb0e Mon Sep 17 00:00:00 2001 From: teutoburg Date: Thu, 15 Jan 2026 14:24:47 +0100 Subject: [PATCH 20/68] Fully remove deprecated functionality All of those had warnings raised for two minor versions, explicitly stating the version of full removal (v0.12 in this case). --- scopesim/__init__.py | 1 - scopesim/effects/data_container.py | 14 +- scopesim/optics/image_plane.py | 67 +++------ scopesim/server/database.py | 36 +---- scopesim/server/example_data_utils.py | 7 - scopesim/server/github_utils.py | 95 ------------ .../tests/tests_optics/test_ImagePlane.py | 139 +++++------------- scopesim/tests/tests_server/test_database.py | 28 ---- .../tests/tests_source/test_source_Source.py | 14 +- 9 files changed, 82 insertions(+), 319 deletions(-) delete mode 100644 scopesim/server/github_utils.py diff --git a/scopesim/__init__.py b/scopesim/__init__.py index 17a330302..f081435df 100644 --- a/scopesim/__init__.py +++ b/scopesim/__init__.py @@ -83,6 +83,5 @@ list_example_data, download_example_data, ) -from .server.database import download_package # remove in v0.12 from .tests.mocks.load_basic_instrument import load_example_optical_train, example_simulation diff --git a/scopesim/effects/data_container.py b/scopesim/effects/data_container.py index f473886c9..217eeec07 100644 --- a/scopesim/effects/data_container.py +++ b/scopesim/effects/data_container.py @@ -64,6 +64,12 @@ class DataContainer: needed in order to save on memory usage. `._file` contains a pointer to the data open FITS file. + + .. versionchanged:: 0.12.0 + + Passing the old-style `file_name` argument now raises a ValueError. + Please use the new-style `filename` instead. + """ meta = None @@ -83,11 +89,9 @@ def __init__( # from scopesim import UserCommands # self.cmds = UserCommands() - if filename is None and "file_name" in kwargs: - warn("The 'file_name' kwarg is deprecated and will raise an error " - "from version 0.12 onwards, please use 'filename' instead!", - DeprecationWarning, stacklevel=2) - filename = kwargs["file_name"] + if "file_name" in kwargs: + raise ValueError("The 'file_name' kwarg is deprecated, please use " + "'filename' instead!") # A !-string filename is immediately resolved, but the file is not yet # searched for. This makes sense, as the filename should end up in the diff --git a/scopesim/optics/image_plane.py b/scopesim/optics/image_plane.py index 794c03fbb..8a4205a82 100644 --- a/scopesim/optics/image_plane.py +++ b/scopesim/optics/image_plane.py @@ -77,38 +77,23 @@ def __init__(self, header, cmds=None, **kwargs): self._sky_wcs = self._get_wcs(header, " ") logger.debug("sky %s", self._sky_wcs) - def add(self, hdus_or_tables, sub_pixel=None, spline_order=None, + def add(self, hdus, sub_pixel=None, spline_order=None, wcs_suffix=""): """ - Add a projection of an image or table files to the canvas. - - .. note:: - If a Table is provided, it must include the following columns: - `x_mm`, `y_mm`, and `flux`. - - Units for the columns should be provided in the - .unit attribute or as an entry in the table's meta - dictionary using this syntax: -
.meta["_unit"] = . - - For example:: - - tbl["x"].unit = u.arcsec # or - tbl.meta[x_unit"] = "deg" - - If no units are given, default units will be assumed. These are: - - - `x`, `y`: `arcsec` - - `flux` : `ph / s / pix` + Add a projection of an image to the canvas. .. versionchanged:: 0.10.0 Adding a table directly to the ImagePlane is deprecated. Use FOV to add tables and image HDUs together before adding them to here. + .. versionchanged:: 0.12.0 + + Support for adding tables fully removed. See note above. + Parameters ---------- - hdus_or_tables : `fits.ImageHDU` or `astropy.Table` + hdus : `fits.ImageHDU` or list thereof The input to be projected onto the image plane. See above. sub_pixel : bool, optional @@ -129,30 +114,24 @@ def add(self, hdus_or_tables, sub_pixel=None, spline_order=None, if spline_order is None: spline_order = from_currsys("!SIM.computing.spline_order", self.cmds) - if isinstance(hdus_or_tables, (list, tuple)): + if isinstance(hdus, (list, tuple)): logger.debug("Adding multiple HDUs to ImagePlane.") - for hdu_or_table in hdus_or_tables: - self.add(hdu_or_table, sub_pixel, spline_order, wcs_suffix) + for hdu in hdus: + self.add(hdu, sub_pixel, spline_order, wcs_suffix) else: - if isinstance(hdus_or_tables, Table): - warn("Adding a table directly to the ImagePlane is deprecated " - "since v0.10.0. Passing a table to ImagePlane.add() will " - "raise an error from version 0.12 onwards. Use FOV to " - "add tables and image HDUs together before.", - DeprecationWarning, stacklevel=2) - self.hdu = add_table_to_imagehdu(hdus_or_tables, self.hdu, - sub_pixel, wcs_suffix) - elif isinstance(hdus_or_tables, fits.ImageHDU): - logger.debug("Adding HDU with shape %d to ImagePlane.", - hdus_or_tables.data.shape) - self.hdu = add_imagehdu_to_imagehdu(hdus_or_tables, self.hdu, - spline_order, wcs_suffix) - if (img_bunit := hdus_or_tables.header.get("BUNIT")) is not None: - if img_bunit != (imp_bunit := self.hdu.header["BUNIT"]): - logger.warning("Added mismatched BUNIT %s to %s.", - img_bunit, imp_bunit) - else: - logger.warning("No BUNIT found in added HDU.") + if not isinstance(hdus, fits.ImageHDU): + raise TypeError("Only ImageHDUs may be added to ImagePlane.") + + logger.debug("Adding HDU with shape %d to ImagePlane.", + hdus.data.shape) + self.hdu = add_imagehdu_to_imagehdu( + hdus, self.hdu, spline_order, wcs_suffix) + if (img_bunit := hdus.header.get("BUNIT")) is not None: + if img_bunit != (imp_bunit := self.hdu.header["BUNIT"]): + logger.warning("Added mismatched BUNIT %s to %s.", + img_bunit, imp_bunit) + else: + logger.info("No BUNIT found in added HDU.") @property def header(self): diff --git a/scopesim/server/database.py b/scopesim/server/database.py index f7d92560d..8f527714b 100644 --- a/scopesim/server/database.py +++ b/scopesim/server/database.py @@ -10,7 +10,6 @@ import httpxyz as httpx from more_itertools import first, last, groupby_transform -from .github_utils import download_github_folder from .download_utils import ( get_server_folder_contents, handle_download, @@ -33,13 +32,6 @@ class PkgNotFoundError(Exception): """Unable to find given package or given release of that package.""" -def get_server_package_list(): - """Deprecated since v0.6.0.""" - raise AttributeError( - "The singular variant of this function has been deprecated since " - "version 0.6.0 of ScopeSim and will be completely removed in v0.12.") - - def _get_package_name(package: str) -> str: return package.split(".", maxsplit=1)[0] @@ -315,17 +307,11 @@ def _download_single_package( save_dir.mkdir(parents=True, exist_ok=True) if "github" in release: - warn("Downloading IRDB packages directly from GitHub is deprecated " - "and will raise an error from ScopeSim version 0.12 onwards. " - "If you really need to use an unreleased version of an IRDB " - "package, please use a local clone of the IRDB repo instead.", - FutureWarning, stacklevel=3) - - base_url = "https://github.com/AstarVienna/irdb/tree/" - github_hash = release.split(":")[-1].split("@")[-1] - pkg_url = f"{base_url}{github_hash}/{pkg_name}" - download_github_folder(repo_url=pkg_url, output_dir=save_dir) - return save_dir.absolute() + raise ValueError( + "Downloading IRDB packages directly from GitHub is deprecated. " + "If you really need to use an unreleased version of an IRDB " + "package, please use a local clone of the IRDB repo instead." + ) zip_name = _get_zipname(pkg_name, release, all_versions) pkg_url = f"{folders_dict[pkg_name]}/{zip_name}" @@ -460,15 +446,3 @@ def check_packages(instrument: str, download_missing: bool) -> None: raise ValueError( f"IRDB package for {instrument} not found, auto-download is " "disabled. Please set package directory or download package.") - - -# ============================================================================== -# Funtions below from from OLD_database.py -# ============================================================================== - -def download_package(*args, **kwargs): - """Deprecated since v0.5.0.""" - raise AttributeError( - "The singular variant of this function has been deprecated since " - "version 0.5.0 of ScopeSim. It will be completely removed in version " - "0.12. Please use ``download_packages`` (plural variant) instead!") diff --git a/scopesim/server/example_data_utils.py b/scopesim/server/example_data_utils.py index ef82bc9d6..3ea1d65b3 100644 --- a/scopesim/server/example_data_utils.py +++ b/scopesim/server/example_data_utils.py @@ -88,13 +88,6 @@ def download_example_data( will be silently passed to the `retriever`. """ - if isinstance(files[0], list): - raise TypeError( - "Passing a list to download_example_data is deprecated. " - "Simply pass filenames as *args, i.e. " - "download_example_data(\"foo.fits\", \"bar.fits\")." - ) - retriever = create_retriever("example_data", save_dir, url) save_paths = [] diff --git a/scopesim/server/github_utils.py b/scopesim/server/github_utils.py deleted file mode 100644 index dbae348c8..000000000 --- a/scopesim/server/github_utils.py +++ /dev/null @@ -1,95 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Used only by the `database` submodule. - -Original comment for these functions: - 2022-04-10 (KL) - Code taken directly from https://github.com/sdushantha/gitdir - Adapted for ScopeSim usage. - Many thanks to the authors! - -""" - -import re -from warnings import warn -from pathlib import Path - -from .download_utils import handle_download, send_get, create_client -from ..utils import get_logger - - -logger = get_logger(__name__) - - -def create_github_url(url: str) -> None: - """ - From the given url, produce a URL compatible with Github's REST API. - - Can handle blob or tree paths. - - .. deprecated:: 0.11.0 - - This function is deprecated and will be removed in version 0.12. - """ - warn("The function ``create_github_url`` is deprecated and will be " - "removed in version 0.12.", DeprecationWarning, stacklevel=2) - - repo_only_url = re.compile(r"https:\/\/github\.com\/[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}\/[a-zA-Z0-9]+$") - re_branch = re.compile("/(tree|blob)/(.+?)/") - - # Check if the given url is a url to a GitHub repo. If it is, tell the - # user to use 'git clone' to download it - if re.match(repo_only_url, url): - message = ("✘ The given url is a complete repository. Use 'git clone'" - " to download the repository") - logger.error(message) - raise ValueError(message) - - # extract the branch name from the given url (e.g master) - branch = re_branch.search(url) - download_dirs = url[branch.end():] - api_url = (url[:branch.start()].replace("github.com", "api.github.com/repos", 1) + - f"/contents/{download_dirs}?ref={branch.group(2)}") - return api_url, download_dirs - - -def download_github_folder(repo_url: str, - output_dir: Path | str = "./") -> None: - """ - Download the files and directories in repo_url. - - Re-written based on the on the download function - `here `_ - - .. deprecated:: 0.11.0 - - This function is deprecated and will be removed in version 0.12. - """ - warn("The function ``download_github_folder`` is deprecated and will be " - "removed in version 0.12.", DeprecationWarning, stacklevel=2) - - output_dir = Path(output_dir) - - # convert repo_url into an api_url - api_url, download_dirs = create_github_url(repo_url) - - # get the contents of the github folder - with create_client("", cached=False) as client: - data = send_get(client, api_url).json() - - # Make the base directories for this GitHub folder - (output_dir / download_dirs).mkdir(parents=True, exist_ok=True) - - for entry in data: - # if the entry is a further folder, walk through it - if entry["type"] == "dir": - download_github_folder(repo_url=entry["html_url"], - output_dir=output_dir) - - # if the entry is a file, download it - elif entry["type"] == "file": - # download the file - save_path = output_dir / entry["path"] - handle_download(client, entry["download_url"], save_path, - entry["path"], padlen=0, disable_bar=True) - logger.info("Downloaded: %s", entry["path"]) diff --git a/scopesim/tests/tests_optics/test_ImagePlane.py b/scopesim/tests/tests_optics/test_ImagePlane.py index 4bf42ccf2..1a8a81190 100644 --- a/scopesim/tests/tests_optics/test_ImagePlane.py +++ b/scopesim/tests/tests_optics/test_ImagePlane.py @@ -564,27 +564,47 @@ def test_mm_image_and_tables_on_large_canvas(self, input_table_mm, class TestImagePlaneAdd: - def test_add_many_tables_and_imagehdus(self, input_table, image_hdu_rect, - image_hdu_square): - tbl1 = deepcopy(input_table) - tbl2 = deepcopy(input_table) + def test_simple_add_imagehdu_conserves_flux(self, image_hdu_square, + image_hdu_rect): + fields = [image_hdu_rect, image_hdu_square] + hdr = imp_utils.get_canvas_header(pixel_scale=1 * u.arcsec, + hdu_or_table_list=fields) + + orig_sum = image_hdu_rect.data.sum() + + print(wcs.WCS(image_hdu_rect)) + print(wcs.WCS(hdr)) + + implane = opt_imp.ImagePlane(hdr) + implane.add(image_hdu_rect) + + if PLOTS: + plt.imshow(image_hdu_rect.data) + x, y = wcs.WCS(image_hdu_rect).wcs_world2pix(0, 0, 1) + print(x, y) + plt.plot(x, y, "ro") + plt.show() + + plt.imshow(implane.data) + x, y = wcs.WCS(image_hdu_rect).wcs_world2pix(0, 0, 1) + print(x, y) + plt.plot(x, y, "ro") + plt.show() - tbl1["y"] -= 50 - tbl2["x"] += 50 - tbl2["y"] += 50 + assert np.sum(implane.data) == approx(orig_sum, rel=1e-2) + def test_add_many_tables_and_imagehdus(self, image_hdu_rect, image_hdu_square): im_hdu = image_hdu_rect im_hdu.header["CRVAL1"] -= 150*u.arcsec.to(u.deg) im_hdu.header["CRVAL2"] += 20*u.arcsec.to(u.deg) - fields = [im_hdu, tbl1, tbl2, image_hdu_square] + fields = [im_hdu, image_hdu_square] hdr = imp_utils.get_canvas_header(fields, pixel_scale=1 * u.arcsec) implane = opt_imp.ImagePlane(hdr) implane.add(fields) - total_flux = np.sum(tbl1["flux"]) + np.sum(tbl2["flux"]) + \ - np.sum(im_hdu.data) + np.sum(image_hdu_square.data) + total_flux = np.sum(im_hdu.data) + np.sum(image_hdu_square.data) assert np.sum(implane.data) == approx(total_flux) if PLOTS: @@ -594,13 +614,6 @@ def test_add_many_tables_and_imagehdus(self, input_table, image_hdu_rect, x, y = imp_utils.val2pix(implane.header, x, y) plt.plot(x, y, "r-") - for tbl in [tbl1, tbl2]: - hdr = imp_utils._make_bounding_header_for_tables(tbl) - xy = imp_utils.calc_footprint(hdr) - x, y = xy[:, 0], xy[:, 1] - x, y = imp_utils.val2pix(implane.header, x, y) - plt.plot(x, y, "r-") - x0, y0 = imp_utils.val2pix(implane.header, 0, 0) plt.plot(x0, y0, "ro") plt.gca().set_aspect(1) @@ -608,29 +621,21 @@ def test_add_many_tables_and_imagehdus(self, input_table, image_hdu_rect, plt.imshow(implane.data, origin="lower", norm=LogNorm()) plt.show() - def test_add_many_mm_tables_and_imagehdus(self, input_table_mm, - image_hdu_rect_mm, - image_hdu_square_mm): + def test_add_many_mm_tables_and_imagehdus( + self, image_hdu_rect_mm, image_hdu_square_mm): image_hdu_rect = image_hdu_rect_mm image_hdu_square = image_hdu_square_mm - tbl1 = deepcopy(input_table_mm) - tbl2 = deepcopy(input_table_mm) - - tbl1["y_mm"] -= 50 - tbl2["x_mm"] += 50 - tbl2["y_mm"] += 50 im_hdu = image_hdu_rect im_hdu.header["CRVAL1D"] -= 150 # mm im_hdu.header["CRVAL2D"] += 20 - fields = [im_hdu, tbl1, tbl2, image_hdu_square] + fields = [im_hdu, image_hdu_square] hdr = imp_utils.get_canvas_header(fields, pixel_scale=1*u.mm) implane = opt_imp.ImagePlane(hdr) implane.add(fields, wcs_suffix="D") - total_flux = np.sum(tbl1["flux"]) + np.sum(tbl2["flux"]) + \ - np.sum(im_hdu.data) + np.sum(image_hdu_square.data) + total_flux = np.sum(im_hdu.data) + np.sum(image_hdu_square.data) assert np.sum(implane.data) == approx(total_flux) if PLOTS: @@ -640,14 +645,6 @@ def test_add_many_mm_tables_and_imagehdus(self, input_table_mm, x, y = imp_utils.val2pix(implane.header, x, y, "D") plt.plot(x, y, "r-") - for tbl in [tbl1, tbl2]: - hdr = imp_utils._make_bounding_header_for_tables(tbl, - pixel_scale=1*u.mm) - xy = imp_utils.calc_footprint(hdr, "D") - x, y = xy[:, 0], xy[:, 1] - x, y = imp_utils.val2pix(implane.header, x, y, "D") - plt.plot(x, y, "r-") - x0, y0 = imp_utils.val2pix(implane.header, 0, 0, "D") plt.plot(x0, y0, "ro") plt.gca().set_aspect(1) @@ -828,7 +825,7 @@ def test_header_does_not_contain_negative_naxis_keywords(self): # -class TestAddTableToImageHDU: +class TestAddTableToImageHDU2: @pytest.mark.parametrize("xpix, ypix, value", [(51, 51, 2), (48, 51, 3), @@ -930,69 +927,3 @@ def test_initialises_with_header_with_hdu(self, image_hdu_square, def test_throws_error_if_header_does_not_have_valid_wcs(self): with pytest.raises(ValueError): opt_imp.ImagePlane(fits.Header()) - - -class TestImagePlaneAdd: - def test_simple_add_imagehdu_conserves_flux(self, image_hdu_square, - image_hdu_rect): - fields = [image_hdu_rect, image_hdu_square] - hdr = imp_utils.get_canvas_header(pixel_scale=1 * u.arcsec, - hdu_or_table_list=fields) - - orig_sum = image_hdu_rect.data.sum() - - print(wcs.WCS(image_hdu_rect)) - print(wcs.WCS(hdr)) - - implane = opt_imp.ImagePlane(hdr) - implane.add(image_hdu_rect) - - if PLOTS: - plt.imshow(image_hdu_rect.data) - x, y = wcs.WCS(image_hdu_rect).wcs_world2pix(0, 0, 1) - print(x, y) - plt.plot(x, y, "ro") - plt.show() - - plt.imshow(implane.data) - x, y = wcs.WCS(image_hdu_rect).wcs_world2pix(0, 0, 1) - print(x, y) - plt.plot(x, y, "ro") - plt.show() - - assert np.sum(implane.data) == approx(orig_sum, rel=1e-2) - - # TODO: rm this test once deprecation is complete - @pytest.mark.filterwarnings("ignore:Adding a table directly*:DeprecationWarning") - def test_simple_add_table_conserves_flux(self, image_hdu_rect): - x = [75, -75]*u.arcsec - y = [0, 0]*u.arcsec - flux = [30, 20] * u.Unit("ph s-1") - tbl = Table(names=["x", "y", "flux"], data=[x, y, flux]) - - hdr = imp_utils.get_canvas_header(pixel_scale=0.1 * u.arcsec, - hdu_or_table_list=[image_hdu_rect, - tbl]) - implane = opt_imp.ImagePlane(hdr) - implane.add(tbl) - assert np.isclose(np.sum(implane.data), np.sum(flux.value)) - - # TODO: rm this test once deprecation is complete - @pytest.mark.filterwarnings("ignore:Adding a table directly*:DeprecationWarning") - def test_compound_add_image_and_table_conserves_flux(self, image_hdu_rect): - x = [75, -75]*u.arcsec - y = [0, 0]*u.arcsec - flux = [30, 20] * u.Unit("ph s-1") - tbl = Table(names=["x", "y", "flux"], data=[x, y, flux]) - - hdr = imp_utils.get_canvas_header(pixel_scale=0.1 * u.arcsec, - hdu_or_table_list=[image_hdu_rect, - tbl]) - in_sum = np.sum(flux.value) + np.sum(image_hdu_rect.data) - - implane = opt_imp.ImagePlane(hdr) - implane.add(tbl) - implane.add(image_hdu_rect) - out_sum = np.sum(implane.data) - - assert out_sum == approx(in_sum, rel=5e-3) diff --git a/scopesim/tests/tests_server/test_database.py b/scopesim/tests/tests_server/test_database.py index efb6858f0..9bf9515b3 100644 --- a/scopesim/tests/tests_server/test_database.py +++ b/scopesim/tests/tests_server/test_database.py @@ -8,7 +8,6 @@ from scopesim.server import ServerError from scopesim.server import database as db from scopesim.server import example_data_utils as dbex -from scopesim.server import github_utils as dbgh @pytest.fixture(scope="class") @@ -161,33 +160,6 @@ def test_downloads_github_version_of_package_with_at_symbol(self): assert filename.exists() -@pytest.mark.webtest(github=True) -@pytest.mark.filterwarnings("ignore:The function*:DeprecationWarning") -class TestDownloadGithubFolder: - def test_downloads_current_package(self): - with TemporaryDirectory() as tmpdir: - # tmpdir = "." - url = "https://github.com/AstarVienna/irdb/tree/dev_master/MICADO" - dbgh.download_github_folder(url, output_dir=tmpdir) - filename = Path(tmpdir, "MICADO", "default.yaml") - - assert filename.exists() - - def test_downloads_with_old_commit_hash(self): - with TemporaryDirectory() as tmpdir: - url = "https://github.com/AstarVienna/irdb/tree/728761fc76adb548696205139e4e9a4260401dfc/ELT" - dbgh.download_github_folder(url, output_dir=tmpdir) - filename = Path(tmpdir, "ELT", "EC_sky_25.tbl") - - assert filename.exists() - - def test_throws_for_bad_url(self): - with TemporaryDirectory() as tmpdir: - url = "https://github.com/AstarVienna/irdb/tree/bogus/MICADO" - with pytest.raises(ServerError): - dbgh.download_github_folder(url, output_dir=tmpdir) - - def test_registry_files(): registry = (Path(__file__).parent.parent.parent / "server/example_data_registry.txt") diff --git a/scopesim/tests/tests_source/test_source_Source.py b/scopesim/tests/tests_source/test_source_Source.py index b7da0cf42..feea28e44 100644 --- a/scopesim/tests/tests_source/test_source_Source.py +++ b/scopesim/tests/tests_source/test_source_Source.py @@ -262,14 +262,20 @@ def test_empty_source_is_the_additive_identity(self, image_source): class TestSourceImageInRange: - # TODO: figure out why this occurs here, then solve it - @pytest.mark.filterwarnings("ignore:Adding a table directly*:DeprecationWarning") + @pytest.mark.skip(reason=( + "This was the only place where the 'add table to ImagePlane' branch " + "was still used. Figure out what actually gets tested here and then " + "try to do it in another way." + )) def test_returns_an_image_plane_object(self, table_source): im = table_source.image_in_range(1*u.um, 2*u.um) assert isinstance(im, ImagePlane) - # TODO: figure out why this occurs here, then solve it - @pytest.mark.filterwarnings("ignore:Adding a table directly*:DeprecationWarning") + @pytest.mark.skip(reason=( + "This was the only place where the 'add table to ImagePlane' branch " + "was still used. Figure out what actually gets tested here and then " + "try to do it in another way." + )) def test_flux_from_table_on_image_is_as_expected(self, table_source): ph = table_source.photons_in_range(1*u.um, 2*u.um) ref = table_source.fields[0].field["ref"] From 4bbb0f35b128e8ee7a07b5c267dbbf833f926260 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Fri, 24 Jul 2026 14:53:55 +0200 Subject: [PATCH 21/68] Don't test deprecated functionality --- scopesim/tests/tests_server/test_database.py | 24 -------------------- 1 file changed, 24 deletions(-) diff --git a/scopesim/tests/tests_server/test_database.py b/scopesim/tests/tests_server/test_database.py index 9bf9515b3..55c670f70 100644 --- a/scopesim/tests/tests_server/test_database.py +++ b/scopesim/tests/tests_server/test_database.py @@ -135,30 +135,6 @@ def test_downloads_specific_package(self): assert version_dict["version"] == release - @pytest.mark.webtest(github=True) - @pytest.mark.filterwarnings("ignore:Downloading IRDB packages:FutureWarning") - @pytest.mark.filterwarnings("ignore:The function*:DeprecationWarning") - def test_downloads_github_version_of_package_with_semicolon(self): - release = "github:728761fc76adb548696205139e4e9a4260401dfc" - with TemporaryDirectory() as tmpdir: - db.download_packages("ELT", release=release, - save_dir=tmpdir) - filename = Path(tmpdir, "ELT", "EC_sky_25.tbl") - - assert filename.exists() - - @pytest.mark.webtest(github=True) - @pytest.mark.filterwarnings("ignore:Downloading IRDB packages:FutureWarning") - @pytest.mark.filterwarnings("ignore:The function*:DeprecationWarning") - def test_downloads_github_version_of_package_with_at_symbol(self): - release = "github@728761fc76adb548696205139e4e9a4260401dfc" - with TemporaryDirectory() as tmpdir: - db.download_packages("ELT", release=release, - save_dir=tmpdir) - filename = Path(tmpdir, "ELT", "EC_sky_25.tbl") - - assert filename.exists() - def test_registry_files(): registry = (Path(__file__).parent.parent.parent / From b3a0aca35b7933d4dd2ae9284e96b9ac0811b42c Mon Sep 17 00:00:00 2001 From: Fabian H <73600109+teutoburg@users.noreply.github.com> Date: Fri, 24 Jul 2026 15:11:43 +0200 Subject: [PATCH 22/68] Fix scalar Quantity pattern matching --- scopesim/effects/fits_headers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scopesim/effects/fits_headers.py b/scopesim/effects/fits_headers.py index 590a3b2ab..9110f3331 100644 --- a/scopesim/effects/fits_headers.py +++ b/scopesim/effects/fits_headers.py @@ -402,6 +402,10 @@ def flatten_dict( # Catch any value+comments lists/tuples match val: + # This became necessary in numpy 2.5, to avoid tripping on scalar quantities + case u.Quantity() as value: + # value get bound to variable anyway + comment = "" case [value, str(comment)]: pass # values get bound to variables anyway case value: From e26e34015460e68ca10dcc2cbec08dcb623685d2 Mon Sep 17 00:00:00 2001 From: "astarvienna-bot[bot]" <217837468+myciapp[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 23:46:58 +0000 Subject: [PATCH 23/68] Bumping version from 0.12.0b0.dev0 to 0.12.0b1 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index dd160cd6e..05d77f932 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ScopeSim" -version = "0.12.0b0.dev0" +version = "0.12.0b1" description = "Generalised telescope observation simulator" license = {text = "GPL-3.0-or-later"} authors = [ From 8c3dc6b4cc0a7d848e356125c78520ad56b0e8f9 Mon Sep 17 00:00:00 2001 From: oczoske Date: Tue, 4 Aug 2026 13:04:15 +0200 Subject: [PATCH 24/68] Record row number in aplist --- scopesim/effects/mosaic_trace_list.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/scopesim/effects/mosaic_trace_list.py b/scopesim/effects/mosaic_trace_list.py index 102f7118a..aca1ed625 100644 --- a/scopesim/effects/mosaic_trace_list.py +++ b/scopesim/effects/mosaic_trace_list.py @@ -33,7 +33,7 @@ class MosaicSpectralTraceList(SpectralTraceList): def __init__(self, **kwargs): super().__init__(**kwargs) - self.aplist = self._file["Aperture List"].data + self.aplist = Table(self._file["Aperture List"].data) # TODO: check units or normalise to arcsec self.view = np.array([(self.aplist["right"].max() - self.aplist["left"].min()), @@ -82,7 +82,12 @@ def apply_to(self, obj, **kwargs): # - map each 1D spectrum to detector/fov image = np.zeros((naxis2d, naxis1d), dtype=np.float32) + image_hdr = detwcs.to_header() + image_hdr["BUNIT"] = "ph s-1" + image_hdr.extend(det_header) + jfibs = np.zeros(len(self.aplist), dtype=int) + apnum = 0 for sptid, spt in tqdm(self.spectral_traces.items(), desc="Fiber traces", position=2): theap = self.aplist[self.aplist["id"] == sptid] @@ -114,11 +119,12 @@ def apply_to(self, obj, **kwargs): detdisp = np.diff(detlam, prepend=detlam[0]) image[jfib,] += (spec(detlam) * detdisp).value + jfibs[apnum] = jfib + apnum += 1 - image_hdr = detwcs.to_header() - image_hdr["BUNIT"] = "ph s-1" - image_hdr.extend(det_header) obj.hdu = fits.ImageHDU(data=image, header=image_hdr) + self.aplist.add_column(jfibs, name="jfib") + return obj def make_spectral_traces(self): From 1015cabce9319b0f0cce05f97ba37b5ea6165efe Mon Sep 17 00:00:00 2001 From: oczoske Date: Tue, 4 Aug 2026 18:38:24 +0200 Subject: [PATCH 25/68] MosaicConvertToTable effect --- scopesim/effects/mosaic_trace_list.py | 44 +++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/scopesim/effects/mosaic_trace_list.py b/scopesim/effects/mosaic_trace_list.py index aca1ed625..5620b0b7e 100644 --- a/scopesim/effects/mosaic_trace_list.py +++ b/scopesim/effects/mosaic_trace_list.py @@ -261,3 +261,47 @@ def apply_to(self, det, **kwargs): ]) return det + + +class MosaicConvertToTable(MosaicSpectralTraceList): + """Convert SpectralTrace readout to Table format + + .. versionadded:: PLACEHOLDER_NEXT_RELEASE_VERSION + """ + + required_keys = {"filename"} + z_order: ClassVar[tuple[int, ...]] = (899,) + + def apply_to(self, det, **kwargs): + """Apply to detector readout.""" + if not isinstance(det, Detector): + return det + + image = det._hdu.data + detwcs = WCS(det._hdu.header, key="D") + + # Need wavelength vector + # Initialise Table + ntrace = len(self.spectral_traces) + idarr = [] + xarr = np.zeros(ntrace, dtype=np.float32) + yarr = np.zeros(ntrace, dtype=np.float32) + specarr = [] + lamarr = [] + i = 0 + x_mm = detwcs.all_pix2world(np.arange(image.shape[1]), 1, 0)[0] + for sptid, spt in tqdm(self.spectral_traces.items(), + desc="Fiber traces", position=2): + y_mm = spt.table["y"][0] + jfib = int(detwcs.all_world2pix(0, y_mm, 0)[1]) + idarr.append(sptid) + yarr[i] = (self.aplist['top'][i] + self.aplist['bottom'][i]) / 2 + xarr[i] = (self.aplist['left'][i] + self.aplist['right'][i]) / 2 + specarr.append(image[jfib,]) + lamarr.append(spt.x2lam(x_mm)) + i += 1 + + tab = Table(data=[idarr, xarr, yarr, lamarr, specarr], + names=["id", "x", "y", "wavelength", "flux"]) + det._hdu = fits.BinTableHDU(data=tab) + return det From 7e4a468387c644ca412627d735bc83246f0a390b Mon Sep 17 00:00:00 2001 From: oczoske Date: Thu, 6 Aug 2026 10:57:13 +0200 Subject: [PATCH 26/68] More tests for mosaic_trace_list --- .../tests/mocks/MOSAIC/TRACE_MOS-LR-J.fits | Bin 0 -> 54720 bytes .../tests_effects/test_mosaic_trace_list.py | 62 ++++++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 scopesim/tests/mocks/MOSAIC/TRACE_MOS-LR-J.fits diff --git a/scopesim/tests/mocks/MOSAIC/TRACE_MOS-LR-J.fits b/scopesim/tests/mocks/MOSAIC/TRACE_MOS-LR-J.fits new file mode 100644 index 0000000000000000000000000000000000000000..ef98b21bdab316ceb7135f886826a2689946512b GIT binary patch literal 54720 zcmeI4Uu+{s9mjn=`iD>i1=I(GuFD@aREn=joH)^moa;EwWl>@qdsE$Uh~P^$IhXj4 z*6V8nw1S7i6AvKq&^ORYm3TpwDypMuao_HNQm865DiSJCRcfhJxPMdvp)#|xGn4i0 z@8Wnj=uY|WL%g%!+28*5JLBD*&rV!1mr9Fy!b)4#!u~v1fhP&0W!=vb}h3rYQ!iu5!jk0)+PW_DS z7|nGVxmLFu?OLuWU>5M zw5*vkc+kai@~b!6tG5+&nR~aAD+m0W9xRj>)Qh)NoKx*Fog5pB5O3aG%v+~QJikiI zUUi-7rm&R(xIYA*XK>>+-hC75NIhJN2t+LwOU^0}<{tqP4d)^8MDZ^5is z#qudbb>?Z~%f;LS#(K?ZyGGS@?Rv}A)puPB#^a44;uXxYQE#rxxk({!tyhMQovk%* z<`?HG;rUGj;)yP^Ub8pX=%^Nd`19Ihm_4klIoxtzn5Q%_m|;n%XZw3V@FBP@bVM!;>&Ns z%db|iZrCfW&1xe;ie7#qUM%^s@pK`(l;{5C*qDB^xzUL<9>nwWUcOzZ3kTo;9JqBI z$d>YDYq^|HBoeoM$d-9?HeX&DW5vYb#Gyn&l*f7bBZ=E&ImOGjvl_lW!OM5B8h&|_ zm+xdX{BoL??_xFl@)R$Bl-0lj2jBo4fCF#<4!{9800(X<2f&~8*k_XB#e?$asRPEJ zxqo^-&0hI)&7Tq!jmK*KJT35ZZ2WoCUiW4OM9)v~XEuw-TcX8VE|`{xS9Mm~_G%ow zUGQh8wsDr9aJ2k*dx&`Pov+W^V>@5Lv)yJZhW6m+E8@j>z9L?H?K|nc4>;3w-DW+G z_7(BsJ6{nmzVl6s_cM(R_6jwQ^A+*pJ6{oR&EANi|MB;EQ{MTqnU`(bImcf1@E1P= zev7^LJtE#jY}eCiFTeI#_Qouh_7L&Xapbq-eN@(epJy!A+U|h%89U|^&e!-vQy$D< zGh+F?LN>i{WRLR;PpnnR9B;wQm$T*Ef{`tpSj>yy?A_kLefwsApC@^9 z_u|&ui!a`HSNFS#i+86U80|j$_=~q2?RUF>9R23+I&c0-4hk%%V>@h~r6kGYiHz8z zlo1hz`rR{?=)U@D{YOvFJk@>v&l-sF2 zCleWYEdSX3?wL;XI+XF9$@orD$DF#(q22eCvG558-~b$e18@KizyUaLvpE3%?2Y-o zKPf(+I&8l3fc$yvpz-Hu^Ply9Xbn2pXRYY*5;6ZdK0YMkbr`GTON8wF`O1P9w?(*! z@i_0iHF)-8v7PV!@a%eQ?JIckR&3`RBhTK!*L-vmt^duLmC_vhUrWZAVrk!-oj3Vy ziVrvd2jGB_d4JtoLK9 ze)!`H&tJSp>G$2u^_NxrOYdIXdivKNQ~D>q@2x8R^3tJWKRwT8YKx`&g15Wg|MD}( z{`167q?~x@!io#M|*C-}3{3H~f~ia#rz;LlR0__NXp{_N`* zXTkwE00-az9DoCG01gBl0Dsma4De_0XWbjbn#U(G|GC$ezRNp)pE{QLu^w;7{O7*? z@2z;<{O1(-bFW7z!vQz|2l{b<r}(qd3I6Qs!O<2{;Ye0SOb5K#wWp_Bl;Kf?@3?BpC_W_hu`1my<(sY2jIX#ae(5_a%4fxf7W_9 ze^xrdpS2#&pOsGVXRU|xXRU|xXQdPTS?l5aS?L6S)_OR9)_OR9Ryx6-wI0r&l}_+y zt%vhxspAt4zyUY_2jBo4fCF$K=m7Y$9w~u8gFoxuAlAU2qwz`b=ZOC8H-DatmLK?Y z?-c`OH~`x zf&h4 zI>n!rPVi@?6Z~1~6n|Da!Jnm0@n@wI{8{M)f0jDMpOsGVXQ@;CS?L6S_H~Rf;Q$d`-+uGwsc89uKlffSP=*6=;Gj4_ z^5^ZLnb!I3A?o)xGD;`-v(zd6taO4uOP%7+N+r}(qd3I42f pf Date: Thu, 6 Aug 2026 12:32:48 +0200 Subject: [PATCH 27/68] more tests --- .../tests_effects/test_mosaic_trace_list.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/scopesim/tests/tests_effects/test_mosaic_trace_list.py b/scopesim/tests/tests_effects/test_mosaic_trace_list.py index 5d542829a..87de6b0ea 100644 --- a/scopesim/tests/tests_effects/test_mosaic_trace_list.py +++ b/scopesim/tests/tests_effects/test_mosaic_trace_list.py @@ -7,12 +7,14 @@ import pytest import numpy as np - +from astropy.io import fits from scopesim.effects.mosaic_trace_list import (Transform1D, MosaicSpectralTraceList, MosaicSpectralTrace, MosaicCollapseSpectralTraces, MosaicConvertToTable) +from scopesim.optics.image_plane_utils import header_from_list_of_xy +from scopesim.detector import Detector @pytest.fixture(name="tf1d", scope="class") def fixture_tf1d(): @@ -86,7 +88,19 @@ def fixture_convert2table(): """Instantiate a ConvertToTable""" return MosaicConvertToTable(filename="TRACE_MOS-LR-J.fits") +@pytest.fixture(name="detector", scope="class") +def fixture_detector(): + """Instantiate a detector image""" + rows = np.array([2023, 2031, 2039, 2047, 2055, 2063, 2071]) + xsize, ysize = 50, 4096 + hdr = header_from_list_of_xy([-xsize/2, xsize/2], [-ysize/2, ysize/2], 1, "D") + dtcr = Detector(hdr) + dtcr._hdu.data[rows, :] = 1 + return dtcr +@pytest.fixture(name="apply_collapse1d", scope="class") +def fixture_apply_collapse1d(collapse1d, detector): + return collapse1d.apply_to(detector) @pytest.mark.usefixtures("patch_mock_path_mosaic") class TestSpectralTraceList: @@ -114,5 +128,14 @@ class TestOutputFormats: def test_initialise_collapse1d(self, collapse1d): assert isinstance(collapse1d, MosaicCollapseSpectralTraces) + def test_collapse1d_returns_detector(self, apply_collapse1d): + assert isinstance(apply_collapse1d, Detector) + + def test_collapse1d_detector_has_bintable(self, apply_collapse1d): + assert isinstance(apply_collapse1d._hdu, fits.BinTableHDU) + + def test_collapse1d_gives_correct_result(self, apply_collapse1d): + assert np.all(apply_collapse1d._hdu.data['spectrum'] == 7) + def test_initialise_table(self, convert2table): assert isinstance(convert2table, MosaicConvertToTable) From 08b235854be3afe9365222fffaad714f59124af2 Mon Sep 17 00:00:00 2001 From: oczoske Date: Thu, 6 Aug 2026 13:15:41 +0200 Subject: [PATCH 28/68] more tests --- scopesim/effects/mosaic_trace_list.py | 8 +++- .../tests_effects/test_mosaic_trace_list.py | 40 ++++++++++++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/scopesim/effects/mosaic_trace_list.py b/scopesim/effects/mosaic_trace_list.py index 5620b0b7e..3396e1667 100644 --- a/scopesim/effects/mosaic_trace_list.py +++ b/scopesim/effects/mosaic_trace_list.py @@ -242,6 +242,9 @@ class MosaicCollapseSpectralTraces(MosaicSpectralTraceList): def apply_to(self, det, **kwargs): """Apply to detector readout.""" if not isinstance(det, Detector): + logger.warning("%s applied to object of type %s", + self.__class__.__name__, + type(det)) return det image = det._hdu.data @@ -275,6 +278,9 @@ class MosaicConvertToTable(MosaicSpectralTraceList): def apply_to(self, det, **kwargs): """Apply to detector readout.""" if not isinstance(det, Detector): + logger.warning("%s applied to object of type %s", + self.__class__.__name__, + type(det)) return det image = det._hdu.data @@ -302,6 +308,6 @@ def apply_to(self, det, **kwargs): i += 1 tab = Table(data=[idarr, xarr, yarr, lamarr, specarr], - names=["id", "x", "y", "wavelength", "flux"]) + names=["id", "x", "y", "wavelength", "spectrum"]) det._hdu = fits.BinTableHDU(data=tab) return det diff --git a/scopesim/tests/tests_effects/test_mosaic_trace_list.py b/scopesim/tests/tests_effects/test_mosaic_trace_list.py index 87de6b0ea..dd4ef4b62 100644 --- a/scopesim/tests/tests_effects/test_mosaic_trace_list.py +++ b/scopesim/tests/tests_effects/test_mosaic_trace_list.py @@ -16,6 +16,9 @@ from scopesim.optics.image_plane_utils import header_from_list_of_xy from scopesim.detector import Detector +import logging +LOGGER = logging.getLogger(__name__) + @pytest.fixture(name="tf1d", scope="class") def fixture_tf1d(): """Instantiate a Transform1D""" @@ -67,6 +70,8 @@ def test_fit_gives_correct_coeffs(self): assert tf1d.coeffs == pytest.approx(coeffs) + +### Fixtures to test the classes @pytest.fixture(scope="class") def patch_mock_path_mosaic(mock_dir): mosaic_dir = mock_dir / "MOSAIC" @@ -102,6 +107,11 @@ def fixture_detector(): def fixture_apply_collapse1d(collapse1d, detector): return collapse1d.apply_to(detector) +@pytest.fixture(name="apply_converttotable", scope="class") +def fixture_apply_converttotable(convert2table, detector): + return convert2table.apply_to(detector) + + @pytest.mark.usefixtures("patch_mock_path_mosaic") class TestSpectralTraceList: """Tests for MosaicSpectralTraceList""" @@ -123,11 +133,17 @@ def test_trace_has_transforms(self, tracelist): @pytest.mark.usefixtures("patch_mock_path_mosaic") -class TestOutputFormats: - """Tests for MOSAIC MOS/mIFU output formats""" +class TestCollapse1D: + """Tests for MOSAIC MOS collapse to 1D spectrum""" def test_initialise_collapse1d(self, collapse1d): assert isinstance(collapse1d, MosaicCollapseSpectralTraces) + def test_collapse1d_returns_nondetector(self, collapse1d, caplog): + caplog.set_level(logging.WARNING) + obj = ["Not", "a", "detector"] + assert collapse1d.apply_to(obj) == obj + assert "MosaicCollapseSpectralTraces" in caplog.text + def test_collapse1d_returns_detector(self, apply_collapse1d): assert isinstance(apply_collapse1d, Detector) @@ -137,5 +153,25 @@ def test_collapse1d_detector_has_bintable(self, apply_collapse1d): def test_collapse1d_gives_correct_result(self, apply_collapse1d): assert np.all(apply_collapse1d._hdu.data['spectrum'] == 7) + +@pytest.mark.usefixtures("patch_mock_path_mosaic") +class TestConvertToTable: + """Tests for MOSAIC mIFU table output""" def test_initialise_table(self, convert2table): assert isinstance(convert2table, MosaicConvertToTable) + + def test_converttotable_returns_nondetector(self, convert2table, caplog): + caplog.set_level(logging.WARNING) + obj = {"This": "is not", "a": "detector"} + assert convert2table.apply_to(obj) == obj + assert "MosaicConvertToTable" in caplog.text + + def test_converttotable_returns_detector(self, apply_converttotable): + assert isinstance(apply_converttotable, Detector) + + def test_converttotable_detector_has_bintable(self, apply_converttotable): + assert isinstance(apply_converttotable._hdu, fits.BinTableHDU) + + def test_converttotable_gives_correct_result(self, apply_converttotable): + for row in apply_converttotable._hdu.data: + assert np.all(row['spectrum'] == 1) From 18e03f7214804fc24a81e9ec5e1e4914b2c07a2c Mon Sep 17 00:00:00 2001 From: oczoske Date: Thu, 6 Aug 2026 19:05:17 +0200 Subject: [PATCH 29/68] Combine to a single effect MosaicOutputFormat --- scopesim/effects/mosaic_trace_list.py | 127 ++++++++++-------- .../tests_effects/test_mosaic_trace_list.py | 42 ++++-- 2 files changed, 102 insertions(+), 67 deletions(-) diff --git a/scopesim/effects/mosaic_trace_list.py b/scopesim/effects/mosaic_trace_list.py index 3396e1667..249bc1a11 100644 --- a/scopesim/effects/mosaic_trace_list.py +++ b/scopesim/effects/mosaic_trace_list.py @@ -15,7 +15,7 @@ from .spectral_trace_list import SpectralTraceList from .spectral_trace_list_utils import SpectralTrace -from ..utils import get_logger, quantify, power_vector +from ..utils import get_logger, quantify, power_vector, from_currsys from ..optics.fov import FieldOfView from ..optics.fov_volume_list import FovVolumeList from ..detector import Detector @@ -230,13 +230,24 @@ def gradient(self): return Transform1D(dcoeffs) -class MosaicCollapseSpectralTraces(MosaicSpectralTraceList): - """Collapse SpectralTraces to 1D spectrum. +class MosaicOutputFormat(MosaicSpectralTraceList): + """Determine output format for Mosaic MOS/mIFU data - .. versionadded:: 0.11.0 + format: str + "collapse1d": all fibres are summed into a single spectrum. + The output is a fits table with columns `wavelength` and + `spectrum`. (default for MOS modes) + "table": The output is a fits table with a row for each fibre. The + columns are `id`, `x`, `y`, `wavelength`, `spectrum`. + `x` and `y` give the sky position of the fiber. `wavelength` + and `spectrum` are arrays. (default for mIFU modes) + "image": The output is a (pseudo-)detector image where the + spectrum for each fibre occupies a row. + + .. versionadded:: PLACEHOLDER_NEXT_RELEASE_VERSION """ - required_keys = {"filename"} + required_keys = {"filename", "format"} z_order: ClassVar[tuple[int, ...]] = (899,) def apply_to(self, det, **kwargs): @@ -249,65 +260,63 @@ def apply_to(self, det, **kwargs): image = det._hdu.data detwcs = WCS(det._hdu.header, key="D") - spec = np.zeros(image.shape[1], dtype=np.float32) - for sptid, spt in tqdm(self.spectral_traces.items(), - desc="Fiber traces", position=2): - y_mm = spt.table["y"][0] - jfib = int(detwcs.all_world2pix(0, y_mm, 0)[1]) - spec += image[jfib,] - - x_mm = detwcs.all_pix2world(np.arange(image.shape[1]), 1, 0)[0] - lam = spt.x2lam(x_mm) - det._hdu = fits.BinTableHDU.from_columns([ - fits.Column(name="wavelength", format="D", array=lam, unit="um"), - fits.Column(name="spectrum", format="D", array=spec, unit="ADU"), - ]) - return det + output_format = from_currsys(self.meta["format"], self.cmds) + if output_format == "collapse1d": + spec = np.zeros(image.shape[1], dtype=np.float32) + for sptid, spt in tqdm(self.spectral_traces.items(), + desc="Fiber traces", position=2): + x_mm = detwcs.all_pix2world(np.arange(image.shape[1]), 1, 0)[0] + lam = spt.x2lam(x_mm) + + y_mm = spt.table["y"][0] + jfib = int(detwcs.all_world2pix(0, y_mm, 0)[1]) + spec += image[jfib,] + + det._hdu = fits.BinTableHDU.from_columns([ + fits.Column(name="wavelength", format="D", array=lam, unit="um"), + fits.Column(name="spectrum", format="D", array=spec, unit="ADU"), + ]) + + elif output_format == "table": + ntrace = len(self.spectral_traces) + idarr = [] + xarr = np.zeros(ntrace, dtype=np.float32) + yarr = np.zeros(ntrace, dtype=np.float32) + specarr = [] + lamarr = [] + i = 0 + for sptid, spt in tqdm(self.spectral_traces.items(), + desc="Fiber traces", position=2): + x_mm = detwcs.all_pix2world(np.arange(image.shape[1]), 1, 0)[0] + lam = spt.x2lam(x_mm) -class MosaicConvertToTable(MosaicSpectralTraceList): - """Convert SpectralTrace readout to Table format + y_mm = spt.table["y"][0] + jfib = int(detwcs.all_world2pix(0, y_mm, 0)[1]) + idarr.append(sptid) + xarr[i] = (self.aplist['left'][i] + self.aplist['right'][i]) / 2 + yarr[i] = (self.aplist['top'][i] + self.aplist['bottom'][i]) / 2 + specarr.append(image[jfib,]) + lamarr.append(lam) + i += 1 - .. versionadded:: PLACEHOLDER_NEXT_RELEASE_VERSION - """ + tab = Table(data=[idarr, xarr, yarr, lamarr, specarr], + names=["id", "x", "y", "wavelength", "spectrum"], + units=["", "arcsec", "arcsec", "um", "ADU"]) + det._hdu = fits.BinTableHDU(data=tab) - required_keys = {"filename"} - z_order: ClassVar[tuple[int, ...]] = (899,) + elif output_format == "image": + pass - def apply_to(self, det, **kwargs): - """Apply to detector readout.""" - if not isinstance(det, Detector): - logger.warning("%s applied to object of type %s", - self.__class__.__name__, - type(det)) - return det + else: + logger.warning("Unknown output format: %s", output_format) - image = det._hdu.data - detwcs = WCS(det._hdu.header, key="D") - - # Need wavelength vector - # Initialise Table - ntrace = len(self.spectral_traces) - idarr = [] - xarr = np.zeros(ntrace, dtype=np.float32) - yarr = np.zeros(ntrace, dtype=np.float32) - specarr = [] - lamarr = [] - i = 0 - x_mm = detwcs.all_pix2world(np.arange(image.shape[1]), 1, 0)[0] - for sptid, spt in tqdm(self.spectral_traces.items(), - desc="Fiber traces", position=2): - y_mm = spt.table["y"][0] - jfib = int(detwcs.all_world2pix(0, y_mm, 0)[1]) - idarr.append(sptid) - yarr[i] = (self.aplist['top'][i] + self.aplist['bottom'][i]) / 2 - xarr[i] = (self.aplist['left'][i] + self.aplist['right'][i]) / 2 - specarr.append(image[jfib,]) - lamarr.append(spt.x2lam(x_mm)) - i += 1 - - tab = Table(data=[idarr, xarr, yarr, lamarr, specarr], - names=["id", "x", "y", "wavelength", "spectrum"]) - det._hdu = fits.BinTableHDU(data=tab) return det + + + def __str__(self) -> str: + output_format = from_currsys(self.meta["format"], self.cmds) + msg = (f"{self.__class__.__name__} (\"{self.display_name}\"):" + f" \"{output_format}\"") + return msg diff --git a/scopesim/tests/tests_effects/test_mosaic_trace_list.py b/scopesim/tests/tests_effects/test_mosaic_trace_list.py index dd4ef4b62..da3207e76 100644 --- a/scopesim/tests/tests_effects/test_mosaic_trace_list.py +++ b/scopesim/tests/tests_effects/test_mosaic_trace_list.py @@ -11,8 +11,7 @@ from scopesim.effects.mosaic_trace_list import (Transform1D, MosaicSpectralTraceList, MosaicSpectralTrace, - MosaicCollapseSpectralTraces, - MosaicConvertToTable) + MosaicOutputFormat) from scopesim.optics.image_plane_utils import header_from_list_of_xy from scopesim.detector import Detector @@ -86,12 +85,17 @@ def fixture_tracelist(): @pytest.fixture(name="collapse1d", scope="class") def fixture_collapse1d(): """Instantiate a CollapseSpectralTrace""" - return MosaicCollapseSpectralTraces(filename="TRACE_MOS-LR-J.fits") + return MosaicOutputFormat(filename="TRACE_MOS-LR-J.fits", format="collapse1d") @pytest.fixture(name="convert2table", scope="class") def fixture_convert2table(): """Instantiate a ConvertToTable""" - return MosaicConvertToTable(filename="TRACE_MOS-LR-J.fits") + return MosaicOutputFormat(filename="TRACE_MOS-LR-J.fits", format="table") + +@pytest.fixture(name="image", scope="class") +def fixture_image(): + """Instantiate OutputFormat with "image". """ + return MosaicOutputFormat(filename="TRACE_MOS-LR-J.fits", format="image") @pytest.fixture(name="detector", scope="class") def fixture_detector(): @@ -111,6 +115,10 @@ def fixture_apply_collapse1d(collapse1d, detector): def fixture_apply_converttotable(convert2table, detector): return convert2table.apply_to(detector) +@pytest.fixture(name="apply_image", scope="class") +def fixture_apply_image(image, detector): + return image.apply_to(detector) + @pytest.mark.usefixtures("patch_mock_path_mosaic") class TestSpectralTraceList: @@ -136,13 +144,13 @@ def test_trace_has_transforms(self, tracelist): class TestCollapse1D: """Tests for MOSAIC MOS collapse to 1D spectrum""" def test_initialise_collapse1d(self, collapse1d): - assert isinstance(collapse1d, MosaicCollapseSpectralTraces) + assert isinstance(collapse1d, MosaicOutputFormat) def test_collapse1d_returns_nondetector(self, collapse1d, caplog): caplog.set_level(logging.WARNING) obj = ["Not", "a", "detector"] assert collapse1d.apply_to(obj) == obj - assert "MosaicCollapseSpectralTraces" in caplog.text + assert "MosaicOutputFormat" in caplog.text def test_collapse1d_returns_detector(self, apply_collapse1d): assert isinstance(apply_collapse1d, Detector) @@ -158,13 +166,13 @@ def test_collapse1d_gives_correct_result(self, apply_collapse1d): class TestConvertToTable: """Tests for MOSAIC mIFU table output""" def test_initialise_table(self, convert2table): - assert isinstance(convert2table, MosaicConvertToTable) + assert isinstance(convert2table, MosaicOutputFormat) def test_converttotable_returns_nondetector(self, convert2table, caplog): caplog.set_level(logging.WARNING) obj = {"This": "is not", "a": "detector"} assert convert2table.apply_to(obj) == obj - assert "MosaicConvertToTable" in caplog.text + assert "MosaicOutputFormat" in caplog.text def test_converttotable_returns_detector(self, apply_converttotable): assert isinstance(apply_converttotable, Detector) @@ -175,3 +183,21 @@ def test_converttotable_detector_has_bintable(self, apply_converttotable): def test_converttotable_gives_correct_result(self, apply_converttotable): for row in apply_converttotable._hdu.data: assert np.all(row['spectrum'] == 1) + +@pytest.mark.usefixtures("patch_mock_path_mosaic") +class TestImage: + """Tests for MOSAIC MOS leaving detector image""" + def test_initialise_image(self, image): + assert isinstance(image, MosaicOutputFormat) + + def test_image_returns_nondetector(self, image, caplog): + caplog.set_level(logging.WARNING) + obj = ["Not", "a", "detector"] + assert image.apply_to(obj) == obj + assert "MosaicOutputFormat" in caplog.text + + def test_image_returns_detector(self, apply_image): + assert isinstance(apply_image, Detector) + + def test_image_detector_has_image(self, apply_image): + assert isinstance(apply_image._hdu, fits.ImageHDU) From ec803e30fcd7e7b806f38d20dad669ee8057af10 Mon Sep 17 00:00:00 2001 From: oczoske Date: Fri, 7 Aug 2026 17:43:50 +0200 Subject: [PATCH 30/68] Implement review suggestions --- scopesim/effects/mosaic_trace_list.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scopesim/effects/mosaic_trace_list.py b/scopesim/effects/mosaic_trace_list.py index 249bc1a11..a5071266c 100644 --- a/scopesim/effects/mosaic_trace_list.py +++ b/scopesim/effects/mosaic_trace_list.py @@ -286,9 +286,10 @@ def apply_to(self, det, **kwargs): yarr = np.zeros(ntrace, dtype=np.float32) specarr = [] lamarr = [] - i = 0 - for sptid, spt in tqdm(self.spectral_traces.items(), - desc="Fiber traces", position=2): + for i, (sptid, spt) in enumerate(tqdm( + self.spectral_traces.items(), + desc="Fiber traces", + position=2)): x_mm = detwcs.all_pix2world(np.arange(image.shape[1]), 1, 0)[0] lam = spt.x2lam(x_mm) @@ -299,7 +300,6 @@ def apply_to(self, det, **kwargs): yarr[i] = (self.aplist['top'][i] + self.aplist['bottom'][i]) / 2 specarr.append(image[jfib,]) lamarr.append(lam) - i += 1 tab = Table(data=[idarr, xarr, yarr, lamarr, specarr], names=["id", "x", "y", "wavelength", "spectrum"], @@ -310,7 +310,7 @@ def apply_to(self, det, **kwargs): pass else: - logger.warning("Unknown output format: %s", output_format) + logger.error("Unknown output format: %s", output_format) return det From e039e253ab1f377c4592d59359172fdf7ee7c8ad Mon Sep 17 00:00:00 2001 From: oczoske Date: Fri, 7 Aug 2026 17:52:52 +0200 Subject: [PATCH 31/68] remove obsolete code and abstract method --- scopesim/effects/mosaic_trace_list.py | 5 ----- scopesim/effects/spectral_trace_list.py | 7 ------- scopesim/tests/tests_effects/test_SpectralTraceList.py | 6 +----- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/scopesim/effects/mosaic_trace_list.py b/scopesim/effects/mosaic_trace_list.py index a5071266c..8b23e3ad3 100644 --- a/scopesim/effects/mosaic_trace_list.py +++ b/scopesim/effects/mosaic_trace_list.py @@ -86,8 +86,6 @@ def apply_to(self, obj, **kwargs): image_hdr["BUNIT"] = "ph s-1" image_hdr.extend(det_header) - jfibs = np.zeros(len(self.aplist), dtype=int) - apnum = 0 for sptid, spt in tqdm(self.spectral_traces.items(), desc="Fiber traces", position=2): theap = self.aplist[self.aplist["id"] == sptid] @@ -119,11 +117,8 @@ def apply_to(self, obj, **kwargs): detdisp = np.diff(detlam, prepend=detlam[0]) image[jfib,] += (spec(detlam) * detdisp).value - jfibs[apnum] = jfib - apnum += 1 obj.hdu = fits.ImageHDU(data=image, header=image_hdr) - self.aplist.add_column(jfibs, name="jfib") return obj diff --git a/scopesim/effects/spectral_trace_list.py b/scopesim/effects/spectral_trace_list.py index d455fc088..2b4e8980e 100644 --- a/scopesim/effects/spectral_trace_list.py +++ b/scopesim/effects/spectral_trace_list.py @@ -291,9 +291,6 @@ def rectify_traces(self, hdulist, xi_min=None, xi_max=None, interps=None, This method creates an HDU list with one extension per spectral trace, i.e. it essentially treats all traces independently. - For the case of an IFU where the traces correspond to spatial - slices for the same wavelength range, use method `rectify_cube` - (not yet implemented). Parameters ---------- @@ -374,10 +371,6 @@ def rectify_traces(self, hdulist, xi_min=None, xi_max=None, interps=None, return outhdul - def rectify_cube(self, hdulist): - """Rectify traces and combine into a cube.""" - raise NotImplementedError() - def plot(self, wave_min=None, wave_max=None, axes=None, **kwargs): """Plot every spectral trace in the spectral trace list. diff --git a/scopesim/tests/tests_effects/test_SpectralTraceList.py b/scopesim/tests/tests_effects/test_SpectralTraceList.py index fbc460ab3..ca634a08e 100644 --- a/scopesim/tests/tests_effects/test_SpectralTraceList.py +++ b/scopesim/tests/tests_effects/test_SpectralTraceList.py @@ -72,11 +72,7 @@ def fixture_spectral_trace_list(): """Instantiate a SpectralTraceList""" return SpectralTraceList(hdulist=tlo.make_trace_hdulist()) -class TestRectification: - def test_rectify_cube_not_implemented(self, spectral_trace_list): - hdulist = fits.HDUList() - with pytest.raises(NotImplementedError): - spectral_trace_list.rectify_cube(hdulist) +#class TestRectification: # def test_rectify_traces_needs_ximin_and_ximax(self, spectral_trace_list): # hdulist = fits.HDUList([fits.PrimaryHDU()]) From 7d5d4f77a04c23635e472b1153b4d1c5850d169c Mon Sep 17 00:00:00 2001 From: teutoburg Date: Mon, 10 Aug 2026 21:11:44 +0200 Subject: [PATCH 32/68] Drop support for Python 3.10 and 3.11 Also fix docutils deprecation. --- poetry.lock | 472 +++++++++------------------------- pyproject.toml | 24 +- scopesim/reports/rst_utils.py | 2 +- scopesim/utils.py | 2 +- 4 files changed, 141 insertions(+), 359 deletions(-) diff --git a/poetry.lock b/poetry.lock index fd62e1ccd..ddc8210ad 100644 --- a/poetry.lock +++ b/poetry.lock @@ -33,23 +33,20 @@ files = [ [[package]] name = "anisocado" -version = "0.5.0" +version = "0.6.0" description = "Generate off-axis SCAO PSFs for the ELT" optional = false -python-versions = "<3.15,>=3.10" +python-versions = "<3.15,>=3.12" groups = ["main"] files = [ - {file = "anisocado-0.5.0-py3-none-any.whl", hash = "sha256:d96747ea0661779cd317f05aa4a15fd1f2d896be0eef8246b1718ce23d70d1b0"}, - {file = "anisocado-0.5.0.tar.gz", hash = "sha256:f386b68b89b586c7835d2937dff80f64d3d46f32a369701abed7902220dc173a"}, + {file = "anisocado-0.6.0-py3-none-any.whl", hash = "sha256:711f55a9ca4dc43e87d22abf2b91756cc14b5e46769d87e7df448df5e7257f84"}, + {file = "anisocado-0.6.0.tar.gz", hash = "sha256:c8dd96a5d57fc87d6d9f9b221a723a00218cecdc4942a5a3477f76bb62ff0aa2"}, ] [package.dependencies] -astropy = ">=6.1.7,<8.0.0" -matplotlib = ">=3.10.1,<4.0.0" -numpy = [ - {version = ">=1.26.4,<2.3.0", markers = "python_version >= \"3.10\" and python_version < \"3.13\""}, - {version = ">=2.2.6,<3.0.0", markers = "python_version >= \"3.13\""}, -] +astropy = ">=7.0.2,<9.0.0" +matplotlib = ">=3.10.9,<4.0.0" +numpy = ">=2.2.6,<3.0.0" [[package]] name = "anyio" @@ -64,7 +61,6 @@ files = [ ] [package.dependencies] -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" @@ -167,14 +163,14 @@ test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock [[package]] name = "astar-utils" -version = "0.6.0" +version = "0.7.0" description = "Contains commonly-used utilities for AstarVienna's projects." optional = false -python-versions = "<3.15,>=3.10" +python-versions = "<3.15,>=3.12" groups = ["main"] files = [ - {file = "astar_utils-0.6.0-py3-none-any.whl", hash = "sha256:be49459ad232e5fb49373dcb438524da4172da40317624befe19e80c0f2cc156"}, - {file = "astar_utils-0.6.0.tar.gz", hash = "sha256:67df5126af37c76ed8f357ddc399f7264958148676f0cb7f61e617f440452326"}, + {file = "astar_utils-0.7.0-py3-none-any.whl", hash = "sha256:f5b3a9e63ba54b75824dfe37fa885680c835c6a25976684f298a8fd517743872"}, + {file = "astar_utils-0.7.0.tar.gz", hash = "sha256:049e837889ba759df71b707ca282ab0737bf990caddea7854f9f06e1d94326a7"}, ] [package.dependencies] @@ -184,57 +180,41 @@ pyyaml = ">=6.0.3,<7.0.0" [[package]] name = "astropy" -version = "6.1.7" +version = "7.2.2" description = "Astronomy and astrophysics core library" optional = false -python-versions = ">=3.10" +python-versions = ">=3.11" groups = ["main"] files = [ - {file = "astropy-6.1.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be954c5f7707a089609053665aeb76493b79e5c4753c39486761bc6d137bf040"}, - {file = "astropy-6.1.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5e48df5ab2e3e521e82a7233a4b1159d071e64e6cbb76c45415dc68d3b97af1"}, - {file = "astropy-6.1.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55c78252633c644361e2f7092d71f80ef9c2e6649f08d97711d9f19af514aedc"}, - {file = "astropy-6.1.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:985e5e74489d23f1a11953b6b283fccde3f46cb6c68fee4f7228e5f6d8350ba9"}, - {file = "astropy-6.1.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dc2ea28ed41a3d92c39b1481d9c5be016ae58d68f144f3fd8cecffe503525bab"}, - {file = "astropy-6.1.7-cp310-cp310-win32.whl", hash = "sha256:4e4badadd8dfa5dca08fd86e9a50a3a91af321975859f5941579e6b7ce9ba199"}, - {file = "astropy-6.1.7-cp310-cp310-win_amd64.whl", hash = "sha256:8d7f6727689288ee08fc0a4a297fc7e8089d01718321646bd00fea0906ad63dc"}, - {file = "astropy-6.1.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:09edca01276ee63f7b2ff511da9bfb432068ba3242e27ef27d76e5a171087b7e"}, - {file = "astropy-6.1.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:072f62a67992393beb016dc80bee8fb994fda9aa69e945f536ed8ac0e51291e6"}, - {file = "astropy-6.1.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2706156d3646f9c9a7fc810475d8ab0df4c717beefa8326552576a0f8ddca20"}, - {file = "astropy-6.1.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcd99e627692f8e58bb3097d330bfbd109a22e00dab162a67f203b0a0601ad2c"}, - {file = "astropy-6.1.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b0ebbcb637b2e9bcb73011f2b7890d7a3f5a41b66ccaad7c28f065e81e28f0b2"}, - {file = "astropy-6.1.7-cp311-cp311-win32.whl", hash = "sha256:192b12ede49cd828362ab1a6ede2367fe203f4d851804ec22fa92e009a524281"}, - {file = "astropy-6.1.7-cp311-cp311-win_amd64.whl", hash = "sha256:3cac64bcdf570c947019bd2bc96711eeb2c7763afe192f18c9551e52a6c296b2"}, - {file = "astropy-6.1.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2a8bcbb1306052cc38c9eed2c9331bfafe2582b499a7321946abf74b26eb256"}, - {file = "astropy-6.1.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eaf88878684f9d31aff36475c90d101f4cff22fdd4fd50098d9950fd56994df7"}, - {file = "astropy-6.1.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb8cd231e53556e4eebe0393ea95a8cea6b2ff4187c95ac4ff8b17e7a8da823"}, - {file = "astropy-6.1.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ad36334d138a4f71d6fdcf225a98ad1dad6c343da4362d5a47a71f5c9da3ca9"}, - {file = "astropy-6.1.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dd731c526869d0c68507be7b31dd10871b7c44d310bb5495476505560c83cd33"}, - {file = "astropy-6.1.7-cp312-cp312-win32.whl", hash = "sha256:662bacd7ae42561e038cbd85eea3b749308cf3575611a745b60f034d3350c97a"}, - {file = "astropy-6.1.7-cp312-cp312-win_amd64.whl", hash = "sha256:5b4d02a98a0bf91ff7fd4ef0bd0ecca83c9497338cb88b61ec9f971350688222"}, - {file = "astropy-6.1.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fbeaf04427987c0c6fa2e579eb40011802b06fba6b3a7870e082d5c693564e1b"}, - {file = "astropy-6.1.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ab6e88241a14185b9404b02246329185b70292984aa0616b20a0628dfe4f4ebb"}, - {file = "astropy-6.1.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0529c75565feaabb629946806b4763ae7b02069aeff4c3b56a69e8a9e638500"}, - {file = "astropy-6.1.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c5ec347631da77573fc729ba04e5d89a3bc94500bf6037152a2d0f9965ae1ce"}, - {file = "astropy-6.1.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc496f87aaccaa5c6624acc985b8770f039c5bbe74b120c8ed7bad3698e24e1b"}, - {file = "astropy-6.1.7-cp313-cp313-win32.whl", hash = "sha256:b1e01d534383c038dbf8664b964fa4ea818c7419318830d3c732c750c64115c6"}, - {file = "astropy-6.1.7-cp313-cp313-win_amd64.whl", hash = "sha256:af08cf2b0368f1ea585eb26a55d99a2de9e9b0bd30aba84b5329059c3ec33590"}, - {file = "astropy-6.1.7.tar.gz", hash = "sha256:a405ac186306b6cb152e6df2f7444ab8bd764e4127d7519da1b3ae4dd65357ef"}, + {file = "astropy-7.2.2-cp311-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b082021721761c15e23cc26bca0efdd74e8fce534ace188abff15e95e1144fa9"}, + {file = "astropy-7.2.2-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:7cceacf26c1492a6234e63a2b5a638b55d4ae88b5d899912b4c357bb7ea8ee03"}, + {file = "astropy-7.2.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b0a168ec6b4fa50344b61b0b2f372695f601bdf0ea627b27ec23d9543ef5206"}, + {file = "astropy-7.2.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:82b4270397a8f62ca7d0d4e268c1412b83a401c148f7525ac75ae7fd85790856"}, + {file = "astropy-7.2.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:54cb7e5a866052e075596ac42fabf6166c5e9b49924d0ff5ec3c6c3a9db1592b"}, + {file = "astropy-7.2.2-cp311-abi3-win32.whl", hash = "sha256:c5aa4f94b0761b371157ee56c7779ace3c72de94680dabd8fe72ff6f8aebd700"}, + {file = "astropy-7.2.2-cp311-abi3-win_amd64.whl", hash = "sha256:3e3fd54268be95a5b13113bbe380b4f8b2b6431bd58a6853edb82d4528766b8f"}, + {file = "astropy-7.2.2-cp311-abi3-win_arm64.whl", hash = "sha256:30f989a0d2cba6273d4645a4274e4d29aef842832c14a3cf622bdedb7aa05c6d"}, + {file = "astropy-7.2.2.tar.gz", hash = "sha256:d48d6025636fa1330594603b9c01345561b6da404e2115852a9136db13f455cd"}, ] [package.dependencies] -astropy-iers-data = ">=0.2024.10.28.0.34.7" -numpy = ">=1.23" -packaging = ">=19.0" +astropy-iers-data = ">=0.2026.6.22.1.23.34" +numpy = ">=1.24,<2.7" +packaging = ">=22.0.0" pyerfa = ">=2.0.1.1" -PyYAML = ">=3.13" +PyYAML = ">=6.0.0" [package.extras] -all = ["asdf-astropy (>=0.3)", "astropy[recommended]", "astropy[typing]", "beautifulsoup4", "bleach", "bottleneck", "certifi", "dask[array]", "fsspec[http] (>=2023.4.0)", "h5py", "html5lib", "ipython (>=4.2)", "jplephem", "mpmath", "pandas", "pre-commit", "pyarrow (>=7.0.0)", "pytest (>=7.0)", "pytz", "s3fs (>=2023.4.0)", "sortedcontainers"] -docs = ["Jinja2 (>=3.1.3)", "astropy[recommended]", "matplotlib (>=3.9.1)", "numpy (<2.0)", "pytest (>=7.0)", "sphinx", "sphinx-astropy[confv2] (>=1.9.1)", "sphinx-changelog (>=1.2.0)", "sphinx_design", "sphinxcontrib-globalsubs (>=0.1.1)", "tomli ; python_version < \"3.11\""] -recommended = ["matplotlib (>=3.5.0,!=3.5.2)", "scipy (>=1.8)"] -test = ["pytest (>=7.0)", "pytest-astropy (>=0.10)", "pytest-astropy-header (>=0.2.1)", "pytest-doctestplus (>=0.12)", "pytest-xdist", "threadpoolctl"] -test-all = ["array-api-strict", "astropy[test]", "coverage[toml]", "ipython (>=4.2)", "objgraph", "sgp4 (>=2.3)", "skyfield (>=1.20)"] -typing = ["typing_extensions (>=4.0.0)"] +all = ["asdf-astropy (>=0.3)", "astropy[ipython]", "astropy[jupyter]", "astropy[recommended]", "beautifulsoup4 (>=4.9.3)", "bleach (>=3.2.1)", "bottleneck (>=1.3.3)", "certifi (>=2022.6.15.1)", "dask[dataframe] (>=2024.8.0)", "fsspec[http] (>=2023.4.0)", "h5py (>=3.9.0)", "html5lib (>=1.1)", "jplephem (>=2.17.0)", "mpmath (>=1.2.1)", "pyarrow (>=14.0.2)", "pytz (>=2016.10)", "s3fs (>=2023.4.0)", "sortedcontainers (>=2.1.0)", "uncompresspy (>=0.4.0)"] +dev = ["astropy[docs]", "astropy[recommended]", "astropy[test]", "astropy[typing]"] +dev-all = ["astropy[dev]", "astropy[test-all]", "tox (>=4.22.0)"] +docs = ["Jinja2 (>=3.1.3)", "astropy[recommended]", "matplotlib (!=3.9.0)", "pytest (>=8.0.0)", "sphinx (>=8.2.0,<9)", "sphinx-astropy[confv2] (>=1.9.1)", "sphinx-changelog (>=1.2.0)", "sphinx_design (>=0.6.1)", "sphinxcontrib-globalsubs (>=0.1.1)"] +ipython = ["ipython (>=8.0.0)"] +jupyter = ["astropy[ipython]", "ipydatagrid (>=1.1.13)", "ipykernel (>=6.16.0)", "ipywidgets (>=7.7.3)", "jupyter-core (>=4.11.2)", "pandas (>=2.0.0)"] +recommended = ["matplotlib (>=3.8.0)", "narwhals (>=1.42.0)", "pyparsing (>=2.4.0)", "scipy (>=1.9.2)"] +test = ["coverage (>=6.4.4)", "pre-commit (>=2.9.3)", "pytest (>=8.0.0)", "pytest-astropy (>=0.10.0)", "pytest-astropy-header (>=0.2.1)", "pytest-doctestplus (>=1.4.0)", "pytest-xdist (>=2.5.0)", "threadpoolctl (>=3.0.0)"] +test-all = ["array-api-strict (<2.4) ; python_version < \"3.12\"", "array-api-strict (>=1.0)", "astropy[all]", "astropy[test]", "numpy-quaddtype (>=0.2.2)", "objgraph (>=3.1.2)", "sgp4 (>=2.3)", "skyfield (>=1.42.0)"] +typing = ["narwhals (>=1.42.0)", "pandas-stubs (>=2.0.0)"] [[package]] name = "astropy-iers-data" @@ -283,9 +263,6 @@ files = [ {file = "async_lru-2.0.4-py3-none-any.whl", hash = "sha256:ff02944ce3c288c5be660c42dbcca0742b32c3b279d6dceda655190240b99224"}, ] -[package.dependencies] -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} - [[package]] name = "attrs" version = "23.1.0" @@ -799,9 +776,6 @@ files = [ {file = "coverage-7.13.1.tar.gz", hash = "sha256:b7593fe7eb5feaa3fbb461ac79aac9f9fc0387a5ca8080b0c6fe2ca27b091afd"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] @@ -875,35 +849,16 @@ files = [ [[package]] name = "docutils" -version = "0.21.2" +version = "0.22.4" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.9" groups = ["main", "docs"] files = [ - {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, - {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, + {file = "docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de"}, + {file = "docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968"}, ] -[[package]] -name = "exceptiongroup" -version = "1.3.0" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -groups = ["main", "dev", "docs", "test"] -markers = "python_version == \"3.10\"" -files = [ - {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, - {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "executing" version = "2.0.1" @@ -1026,7 +981,7 @@ description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" groups = ["docs"] -markers = "(platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") and python_version < \"3.13\"" +markers = "python_version == \"3.12\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")" files = [ {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, @@ -1349,7 +1304,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} @@ -1701,7 +1655,6 @@ jupyter-events = ">=0.11.0" jupyter-server-terminals = ">=0.4.4" nbconvert = ">=6.4.4" nbformat = ">=5.3.0" -overrides = {version = ">=5.0", markers = "python_version < \"3.12\""} packaging = ">=22.0" prometheus-client = ">=0.9" pywinpty = {version = ">=2.0.1,<3.0.4 || >3.0.4", markers = "os_name == \"nt\""} @@ -1784,7 +1737,6 @@ jupyterlab-server = ">=2.28.0,<3" notebook-shim = ">=0.2" packaging = "*" setuptools = ">=41.1.0" -tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} tornado = ">=6.2.0" traitlets = "*" @@ -1863,7 +1815,6 @@ mdit-py-plugins = "*" nbformat = "*" packaging = "*" pyyaml = "*" -tomli = {version = "*", markers = "python_version < \"3.11\""} [package.extras] dev = ["autopep8", "black", "flake8", "gitpython", "ipykernel", "isort", "jupyter-fs[fs] (>=1.0)", "jupyter-server (!=2.11)", "marimo (>=0.17.6,<=0.19.4)", "nbconvert", "pre-commit", "pytest", "pytest-asyncio", "pytest-cov (>=2.6.1)", "pytest-randomly", "pytest-xdist", "sphinx", "sphinx-gallery (>=0.8)"] @@ -2154,14 +2105,14 @@ htmlsoup = ["BeautifulSoup4"] [[package]] name = "markdown-it-py" -version = "3.0.0" +version = "4.2.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["dev", "docs"] files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, + {file = "markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a"}, + {file = "markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49"}, ] [package.dependencies] @@ -2169,13 +2120,12 @@ mdurl = ">=0.1,<1.0" [package.extras] benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"] linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] +plugins = ["mdit-py-plugins (>=0.5.0)"] profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "pytest-timeout", "requests"] [[package]] name = "markupsafe" @@ -2247,86 +2197,6 @@ files = [ {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] -[[package]] -name = "matplotlib" -version = "3.10.9" -description = "Python plotting package" -optional = false -python-versions = ">=3.10" -groups = ["main", "dev", "docs"] -markers = "python_version == \"3.10\"" -files = [ - {file = "matplotlib-3.10.9-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77210dce9cb8153dffc967efaae990543392563d5a376d4dd8539bebcb0ed217"}, - {file = "matplotlib-3.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1e7698ac9868428e84d2c967424803b2472ff7167d9d6590d4204ed775343c3b"}, - {file = "matplotlib-3.10.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1aa972116abb4c9d201bf245620b433726cb6856f3bef6a78f776a00f5c92d37"}, - {file = "matplotlib-3.10.9-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae2f11957b27ce53497dd4d7b235c4d4f1faf383dfb39d0c5beb833bff883294"}, - {file = "matplotlib-3.10.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b049278ddce116aaa1c1377ebf58adea909132dfce0281cf7e3a1ea9fc2e2c65"}, - {file = "matplotlib-3.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:82834c3c292d24d3a8aae77cd2d20019de69d692a34a970e4fdb8d33e2ea3dda"}, - {file = "matplotlib-3.10.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:68cfdcede415f7c8f5577b03303dd94526cdb6d11036cecdc205e08733b2d2bb"}, - {file = "matplotlib-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfca0129678bd56379db26c52b5d77ed7de314c047492fbdc763aa7501710cfb"}, - {file = "matplotlib-3.10.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e436d155fa8a3399dc62683f8f5d0e2e50d25d0144a73edd73f82eec8f4abfb"}, - {file = "matplotlib-3.10.9-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56fc0bd271b00025c6edfdc7c2dcd247372c8e1544971d62e1dc7c17367e8bf9"}, - {file = "matplotlib-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5a6104ed666402ba5106d7f36e0e0cdca4e8d7fa4d39708ca88019e2835a2eb"}, - {file = "matplotlib-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:d730e984eddf56974c3e72b6129c7ca462ac38dc624338f4b0b23eb23ecba00f"}, - {file = "matplotlib-3.10.9-cp311-cp311-win_arm64.whl", hash = "sha256:51bf0ddbdc598e060d46c16b5590708f81a1624cefbaaf62f6a81bf9285b8c80"}, - {file = "matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1"}, - {file = "matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320"}, - {file = "matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285"}, - {file = "matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2"}, - {file = "matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf"}, - {file = "matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6"}, - {file = "matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42"}, - {file = "matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f"}, - {file = "matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e"}, - {file = "matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f"}, - {file = "matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838"}, - {file = "matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2"}, - {file = "matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921"}, - {file = "matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8"}, - {file = "matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9"}, - {file = "matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4"}, - {file = "matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc"}, - {file = "matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99"}, - {file = "matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d"}, - {file = "matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8"}, - {file = "matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38"}, - {file = "matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d"}, - {file = "matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f"}, - {file = "matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b"}, - {file = "matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2"}, - {file = "matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716"}, - {file = "matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f"}, - {file = "matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456"}, - {file = "matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe"}, - {file = "matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6"}, - {file = "matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c"}, - {file = "matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4"}, - {file = "matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf"}, - {file = "matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39"}, - {file = "matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c"}, - {file = "matplotlib-3.10.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1872fb212a05b729e649754a72d5da61d03e0554d76e80303b6f83d1d2c0552b"}, - {file = "matplotlib-3.10.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:985f2238880e2e69093f588f5fe2e46771747febf0649f3cf7f7b7480875317f"}, - {file = "matplotlib-3.10.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6640f75af2c6148293caa0a2b39dd806a492dd66c8a8b04035813e33d0fd2585"}, - {file = "matplotlib-3.10.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:42fb814efabe95c06c1994d8ab5a8385f43a249e23badd3ba931d4308e5bca20"}, - {file = "matplotlib-3.10.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f76e640a5268850bfda54b5131b1b1941cc685e42c5fa98ed9f2d64038308cba"}, - {file = "matplotlib-3.10.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3fc0364dfbe1d07f6d15c5ebd0c5bf89e126916e5a8667dd4a7a6e84c36653d4"}, - {file = "matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358"}, -] - -[package.dependencies] -contourpy = ">=1.0.1" -cycler = ">=0.10" -fonttools = ">=4.22.0" -kiwisolver = ">=1.3.1" -numpy = ">=1.23" -packaging = ">=20.0" -pillow = ">=8" -pyparsing = ">=3" -python-dateutil = ">=2.7" - -[package.extras] -dev = ["meson-python (>=0.13.1,<0.17.0)", "pybind11 (>=2.13.2,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7,<10)"] - [[package]] name = "matplotlib" version = "3.11.0" @@ -2334,7 +2204,6 @@ description = "Python plotting package" optional = false python-versions = ">=3.11" groups = ["main", "dev", "docs"] -markers = "python_version >= \"3.11\"" files = [ {file = "matplotlib-3.11.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f857524b442f0f36e641868ce2171aafa88cb0bc0644f4e1d8a5df9b32649fef"}, {file = "matplotlib-3.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:57baa92fdc82948ed716eae6d2579d4d6f40965cd8d2f416755b4a72580a3233"}, @@ -2412,23 +2281,23 @@ traitlets = "*" [[package]] name = "mdit-py-plugins" -version = "0.4.2" +version = "0.6.1" description = "Collection of plugins for markdown-it-py" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["dev", "docs"] files = [ - {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}, - {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}, + {file = "mdit_py_plugins-0.6.1-py3-none-any.whl", hash = "sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d"}, + {file = "mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0"}, ] [package.dependencies] -markdown-it-py = ">=1.0.0,<4.0.0" +markdown-it-py = ">=2.0.0,<5.0.0" [package.extras] code-style = ["pre-commit"] rtd = ["myst-parser", "sphinx-book-theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "pytest-timeout"] [[package]] name = "mdurl" @@ -2454,9 +2323,6 @@ files = [ {file = "mistune-3.3.3.tar.gz", hash = "sha256:c4c6c0c840b8637a2e9b8b6d607eb7c8f00888bf14c754409bcd339e848c2477"}, ] -[package.dependencies] -typing-extensions = {version = "*", markers = "python_version < \"3.11\""} - [[package]] name = "more-itertools" version = "10.2.0" @@ -2500,30 +2366,30 @@ testing = ["beautifulsoup4", "coverage (>=6.4)", "ipykernel (>=5.5)", "ipython ( [[package]] name = "myst-parser" -version = "4.0.0" +version = "5.1.0" description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," optional = false -python-versions = ">=3.10" +python-versions = ">=3.11" groups = ["docs"] files = [ - {file = "myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d"}, - {file = "myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531"}, + {file = "myst_parser-5.1.0-py3-none-any.whl", hash = "sha256:9c91c52b3cdb4d94a6506e4fab4e2f296c7623a0da0dcbe6de1565c3dad67a8a"}, + {file = "myst_parser-5.1.0.tar.gz", hash = "sha256:ab69322dc6719dcc7f296479dbb70181b66df6ed315064f92dbc85c0e1bf2f02"}, ] [package.dependencies] -docutils = ">=0.19,<0.22" +docutils = ">=0.20,<0.23" jinja2 = "*" -markdown-it-py = ">=3.0,<4.0" -mdit-py-plugins = ">=0.4.1,<1.0" +markdown-it-py = ">=4.2,<5.0" +mdit-py-plugins = ">=0.6.1,<1.0" pyyaml = "*" -sphinx = ">=7,<9" +sphinx = ">=8,<10" [package.extras] -code-style = ["pre-commit (>=3.0,<4.0)"] +code-style = ["pre-commit (>=4.0,<5.0)"] linkify = ["linkify-it-py (>=2.0,<3.0)"] -rtd = ["ipython", "sphinx (>=7)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.9.0,<0.10.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] -testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pytest (>=8,<9)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest"] -testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0,<0.7.0)"] +rtd = ["ipython", "sphinx (>=8)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.13.0,<0.14.0)", "sphinxext-rediraffe (>=0.3.0,<0.4.0)"] +testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pygments (<2.21)", "pytest (>=9,<10)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest (>=0.3.0,<0.4.0)"] +testing-docutils = ["pygments", "pytest (>=9,<10)", "pytest-param-files (>=0.6.0,<0.7.0)"] [[package]] name = "nbclient" @@ -2699,7 +2565,7 @@ description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" groups = ["main", "dev", "docs"] -markers = "python_version < \"3.13\"" +markers = "python_version == \"3.12\"" files = [ {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, {file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"}, @@ -2857,20 +2723,6 @@ files = [ [package.dependencies] sphinx = ">=6" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "overrides" -version = "7.7.0" -description = "A decorator to automatically detect mismatch when overriding a method." -optional = false -python-versions = ">=3.6" -groups = ["dev"] -markers = "python_version < \"3.12\"" -files = [ - {file = "overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"}, - {file = "overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a"}, -] [[package]] name = "packaging" @@ -3301,12 +3153,10 @@ files = [ [package.dependencies] colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} iniconfig = ">=1.0.1" packaging = ">=22" pluggy = ">=1.5,<2" pygments = ">=2.7.2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] @@ -3365,7 +3215,7 @@ description = "Python for Window Extensions" optional = false python-versions = "*" groups = ["dev", "docs", "test"] -markers = "platform_python_implementation != \"PyPy\" and sys_platform == \"win32\"" +markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\"" files = [ {file = "pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3"}, {file = "pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b"}, @@ -3661,6 +3511,18 @@ files = [ {file = "rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055"}, ] +[[package]] +name = "roman-numerals" +version = "4.1.0" +description = "Manipulate well-formed Roman numerals" +optional = false +python-versions = ">=3.10" +groups = ["docs"] +files = [ + {file = "roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7"}, + {file = "roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2"}, +] + [[package]] name = "rpds-py" version = "0.27.1" @@ -3826,71 +3688,6 @@ files = [ {file = "rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8"}, ] -[[package]] -name = "scipy" -version = "1.15.3" -description = "Fundamental algorithms for scientific computing in Python" -optional = false -python-versions = ">=3.10" -groups = ["main"] -markers = "python_version == \"3.10\"" -files = [ - {file = "scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c"}, - {file = "scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253"}, - {file = "scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f"}, - {file = "scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92"}, - {file = "scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82"}, - {file = "scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40"}, - {file = "scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e"}, - {file = "scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c"}, - {file = "scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13"}, - {file = "scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b"}, - {file = "scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba"}, - {file = "scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65"}, - {file = "scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1"}, - {file = "scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889"}, - {file = "scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982"}, - {file = "scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9"}, - {file = "scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594"}, - {file = "scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb"}, - {file = "scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019"}, - {file = "scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6"}, - {file = "scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477"}, - {file = "scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c"}, - {file = "scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45"}, - {file = "scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49"}, - {file = "scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e"}, - {file = "scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539"}, - {file = "scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed"}, - {file = "scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759"}, - {file = "scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62"}, - {file = "scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb"}, - {file = "scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730"}, - {file = "scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825"}, - {file = "scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7"}, - {file = "scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11"}, - {file = "scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126"}, - {file = "scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163"}, - {file = "scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8"}, - {file = "scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5"}, - {file = "scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e"}, - {file = "scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb"}, - {file = "scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723"}, - {file = "scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb"}, - {file = "scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4"}, - {file = "scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5"}, - {file = "scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca"}, - {file = "scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf"}, -] - -[package.dependencies] -numpy = ">=1.23.5,<2.5" - -[package.extras] -dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] -doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.19.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"] -test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja ; sys_platform != \"emscripten\"", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] - [[package]] name = "scipy" version = "1.17.1" @@ -3898,7 +3695,6 @@ description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.11" groups = ["main"] -markers = "python_version >= \"3.11\"" files = [ {file = "scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec"}, {file = "scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696"}, @@ -4023,25 +3819,31 @@ files = [ [[package]] name = "skycalc-ipy" -version = "0.7.0" +version = "0.7.1" description = "Get atmospheric spectral information from the ESO skycalc server." optional = false python-versions = "<3.15,>=3.10" groups = ["main"] files = [ - {file = "skycalc_ipy-0.7.0-py3-none-any.whl", hash = "sha256:1c51688f89ce360c3248a35c43d672594d3063e4faaa9426e7bf881c8f50846d"}, - {file = "skycalc_ipy-0.7.0.tar.gz", hash = "sha256:96580d64e0dfd332fb5093ea6d1d44a7b7186ef50e0b290e94473fd3a86f85de"}, + {file = "skycalc_ipy-0.7.1-py3-none-any.whl", hash = "sha256:499e677ea69bbb2141d94d358c1db97c7d1c48130de2172eda308ba73ceef3ac"}, + {file = "skycalc_ipy-0.7.1.tar.gz", hash = "sha256:687812026a88af0a62bce766afbe2ae840a9fd924a60597b72c50096bcd8f8ed"}, ] [package.dependencies] -astar-utils = ">=0.5.0,<1.0" -astropy = ">=6.1.7,<8.0.0" +astar-utils = ">=0.5.3,<1.0" +astropy = [ + {version = ">=6.1.7,<8.0.0", markers = "python_version >= \"3.10\" and python_version < \"3.13\""}, + {version = ">=7.0.2,<8.0.0", markers = "python_version >= \"3.13\""}, +] httpx = ">=0.28.1,<1.0" -numpy = ">=1.26.4,<2.4.0" +numpy = [ + {version = ">=1.26.4,<2.3.0", markers = "python_version >= \"3.10\" and python_version < \"3.13\""}, + {version = ">=2.3.5,<3.0.0", markers = "python_version >= \"3.13\""}, +] pyyaml = ">=6.0.3,<7.0.0" [package.extras] -syn = ["scipy (>=1.15.3,<1.16.0) ; python_version == \"3.10\"", "scipy (>=1.17.0,<1.18.0) ; python_version >= \"3.11\"", "synphot (>=1.6.1,<2.0.0)"] +syn = ["scipy (>=1.15.3,<1.16.0) ; python_version == \"3.10\"", "scipy (>=1.17.0,<1.18.0) ; python_version >= \"3.11\"", "synphot (>=1.7.0,<2.0.0)"] [[package]] name = "sniffio" @@ -4081,26 +3883,27 @@ files = [ [[package]] name = "sphinx" -version = "8.1.3" +version = "9.1.0" description = "Python documentation generator" optional = false -python-versions = ">=3.10" +python-versions = ">=3.12" groups = ["docs"] files = [ - {file = "sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2"}, - {file = "sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927"}, + {file = "sphinx-9.1.0-py3-none-any.whl", hash = "sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978"}, + {file = "sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb"}, ] [package.dependencies] alabaster = ">=0.7.14" babel = ">=2.13" colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} -docutils = ">=0.20,<0.22" +docutils = ">=0.21,<0.23" imagesize = ">=1.3" Jinja2 = ">=3.1" packaging = ">=23.0" Pygments = ">=2.17" requests = ">=2.30.0" +roman-numerals = ">=1.0.0" snowballstemmer = ">=2.2" sphinxcontrib-applehelp = ">=1.0.7" sphinxcontrib-devhelp = ">=1.0.6" @@ -4108,12 +3911,6 @@ sphinxcontrib-htmlhelp = ">=2.0.6" sphinxcontrib-jsmath = ">=1.0.1" sphinxcontrib-qthelp = ">=1.0.6" sphinxcontrib-serializinghtml = ">=1.1.9" -tomli = {version = ">=2", markers = "python_version < \"3.11\""} - -[package.extras] -docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=6.0)", "mypy (==1.11.1)", "pyright (==1.1.384)", "pytest (>=6.0)", "ruff (==0.6.9)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.18.0.20240506)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241005)", "types-requests (==2.32.0.20240914)", "types-urllib3 (==1.26.25.14)"] -test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] [[package]] name = "sphinx-book-theme" @@ -4401,38 +4198,38 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "synphot" -version = "1.6.1" +version = "1.7.0" description = "Synthetic photometry" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "synphot-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0ac5d550f8e906685d6ae842a84376e315c4689c1b97780551256d84246c1a72"}, - {file = "synphot-1.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d1e46fe6da16c1c4bbd7aeecb1995ddb56f67f8f255af69f9e840595acfeaf58"}, - {file = "synphot-1.6.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b458023f80c5e703bee8098ff5a408ef78b55e35dc786d05bfd3c44792a6e6b7"}, - {file = "synphot-1.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58fe1cd739ce365fdb13892b057eb8eab89f955d21d8e0444676df15cd32bb5a"}, - {file = "synphot-1.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:66818542490b8b7c6edf1c7650da80d2ad754a91f2392cfc2124ba8c08d043f2"}, - {file = "synphot-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ca893e5d694ac7bef358a30c5933fdec3a06a006e60567acc6858827778ff01b"}, - {file = "synphot-1.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ad0ea8ae1ed245d42d265931f30d7f6c6ed62ab6639c29e3d55b6d03103e61d2"}, - {file = "synphot-1.6.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:26fd407171f4fb01a10ddb8fe7215d92e6c9d4bcef2c4a38cbc8a848e30a6722"}, - {file = "synphot-1.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:434fd8969c389c9cdc6dfe49f915ba2144218e7b8e3149125c6ecf42ccd279d2"}, - {file = "synphot-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:a9f751fc55c5ff137d3010f3a4cb5436b4d07ff5aac9562cc342bdd53db0dbf6"}, - {file = "synphot-1.6.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:38de46c7eaa98bb8173aa5c8e58bdc5ef68b8da2c939b49d4f847761178b9613"}, - {file = "synphot-1.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b706cb326fbe95cfbf43345c6a28244c026204f9fcaf532d6268b35ec53d50e1"}, - {file = "synphot-1.6.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f956033a3218c4cda459878dc22b9ec7e240b09efd5bef0881b0addb59d51faf"}, - {file = "synphot-1.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e09b21fed5fe83072fb7fa4f03e83f862224e4e12224df8b909a800563255201"}, - {file = "synphot-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:e0948891ef30264a6bbe750bfdc582cb6e6de2f1b8ce9740348cc2815436c941"}, - {file = "synphot-1.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b551a8194cbca165d252b8b232364a5aff0b1e2751313b294c76bcb7fa213b8"}, - {file = "synphot-1.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f1681f9fe06aeaaa0044fa98801bf0fffb521b823c9b46895b7155b4da74f075"}, - {file = "synphot-1.6.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c120eb4d196819592b900da44f6f67bce66de248d30ee6376b2df21d98656bff"}, - {file = "synphot-1.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fdf41e8e4c2f443f73555a2e728436c987b1d0a23baeaaa477c40e4364e4af61"}, - {file = "synphot-1.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:212ad95cb1f664d593ccf6a91573e8dca46fe85b260da3c31883df346c1f586f"}, - {file = "synphot-1.6.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d61432c06fb26c6bd2a294edc4fee09e992d8e2b4f847140a5c97e8fa4d67f5"}, - {file = "synphot-1.6.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:acb6691b6ab0e5b7fa23e1a98e4bf778a8ab18c19f73f5e02d4311537a01eaa9"}, - {file = "synphot-1.6.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8ce8898237eb264453793ecd95b6e44f8f7d0516e753b029eee245e642de640c"}, - {file = "synphot-1.6.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e7f395226e1103c3469a5747fdaf5e13036e6228467a9c39b895acd811ab163"}, - {file = "synphot-1.6.1-cp314-cp314-win_amd64.whl", hash = "sha256:bbcb7f68384b6a6c1981109df9720406adfdd15b455b12a3bd88d082a9832bbd"}, - {file = "synphot-1.6.1.tar.gz", hash = "sha256:eb7c63c450c6cc5ef8db0c9dd41cd95a4647c95f4769acf1d30bb89061a8358b"}, + {file = "synphot-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8cf368c83064f9131a31c19ebc2cd4843888d8447d99fc407625e186009235fa"}, + {file = "synphot-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b2a703ddfd9f4136c9c97d4c96631a05caefbf08015166b2a3abcdfa8648db0"}, + {file = "synphot-1.7.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7560121f6207cba25cbce4573f23cb0163594a72e893edcf82aaa121dee468bc"}, + {file = "synphot-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5bbe1793a56fd6a7dfabb1b5bb81d3c767fec8034909a3ec7b634901f6ddc2f4"}, + {file = "synphot-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:e0d7798cb23442d1f6d118a9744ac509bc6b77b531ad6792066cbdc18510ed09"}, + {file = "synphot-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f4d8fa3681ee71e7e5b2f63d92849bed7836e099b75ac0b6489eb98b25020dde"}, + {file = "synphot-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:39fa4ba3783ea9b04595e4692ca67e7a083cdc4da8f56e0f37c8e7401a2ce2b5"}, + {file = "synphot-1.7.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:70c29b04495f25f8efdbb97edbd8fea716d2bafd50bc6e14cb30b95faafd0ca7"}, + {file = "synphot-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0317763976e264bcff535dc29f93b5a29300eb757629d31f04edcd0a98635901"}, + {file = "synphot-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:60486a6d5887e0f296c16415a73bc231d0fc0211a0543859125a94301e0e59b0"}, + {file = "synphot-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cefe738d1e807a7b79d4bee62df02411dbe25f7f4eeccd91d7b80dc51bcad7c6"}, + {file = "synphot-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c77ffd51ffed10dbad202c64673596d4d4f167534e1e51ad1a4b484297e1736a"}, + {file = "synphot-1.7.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:79e959f44944200725db60925dd89bac3ef57c9ccd57ef74cd63333be215b108"}, + {file = "synphot-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:79139f14a90c6bf6bf1a639f4b2b41f8015a9915388f26b3dac9e1e0b179df14"}, + {file = "synphot-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:a5dd6b012bca6244ee64e4793ffde8024999633601b94f50b077098264771f9e"}, + {file = "synphot-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4420f88b85276e2fe7f7a5c9e5d18bc66a49c36a55e360399e4fda75f2af4e51"}, + {file = "synphot-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:90790ddede7354b2964f3015ae8e57344ceab2a3e1a50d76365123880f6c941e"}, + {file = "synphot-1.7.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6bf64e62a6a34bc4072a6f11ab9671283fa3211ea8b71a9345eb006dabb70a4d"}, + {file = "synphot-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e81280915f944213ec2d5f56c6d4dcaf3f59901c9aa8dac86bba1e088abd0e63"}, + {file = "synphot-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:5afb08da9a634af85dbbb753ba70ac468baeeff2bd02d7cb4fd08a7df4dbe701"}, + {file = "synphot-1.7.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7d57b670cd1e1d1a820ca32b478b62f3e2550bb1b119d3ad6c3fa894ee8e0e3a"}, + {file = "synphot-1.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9062121c87f4287cba8dcc29bab05bc1a1f471219760b90741f8732922baf4e7"}, + {file = "synphot-1.7.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:43e18635ab59d0334f0f2f32cbc828a79851e36475a974de97fcbd332f227048"}, + {file = "synphot-1.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:452167a1bb67354b92b28dfebd539ffa55267725dd2f0e254a18ccf55645482b"}, + {file = "synphot-1.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:1cc51eec85bcc6869a0c779200f449a2769f6ded48a76c6f24cad8498decf81b"}, + {file = "synphot-1.7.0.tar.gz", hash = "sha256:acd0fff408008a93c43bfbaf27e7c8ccee271d644d62152e4ad2b7462619f9dc"}, ] [package.dependencies] @@ -4501,19 +4298,6 @@ webencodings = ">=0.4" doc = ["sphinx", "sphinx_rtd_theme"] test = ["flake8", "isort", "pytest"] -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -groups = ["dev", "docs", "test"] -markers = "python_version == \"3.10\"" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - [[package]] name = "tornado" version = "6.5.7" @@ -4715,5 +4499,5 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" -python-versions = ">=3.10,<3.15" -content-hash = "79c958fc36902b19c8dc961de5168fc6273168bc2f026bc1a511101367a50223" +python-versions = ">=3.12,<3.15" +content-hash = "6e74a26a9b71290df95b18c5dc705d907ae78f1e3f6852bbbd935926e5abc637" diff --git a/pyproject.toml b/pyproject.toml index 05d77f932..d2a90ee75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ScopeSim" -version = "0.12.0b1" +version = "0.12.0b2.dev0" description = "Generalised telescope observation simulator" license = {text = "GPL-3.0-or-later"} authors = [ @@ -14,18 +14,16 @@ maintainers = [ readme = "README.md" keywords = [ "astronomy" ] dynamic = [ "classifiers" ] -requires-python = ">=3.10,<3.15" +requires-python = ">=3.12,<3.15" dependencies = [ - "numpy (>=1.26.4,<2.3.0) ; python_version >= '3.10' and python_version < '3.13'", - "numpy (>=2.3.5,<3.0.0) ; python_version >= '3.13'", - "scipy (>=1.15.3,<1.16.0) ; python_version >= '3.10' and python_version < '3.11'", - "scipy (>=1.17.0,<1.18.0) ; python_version >= '3.11'", + "numpy (>=2.2.6,<3.0.0)", + "scipy (>=1.17.0,<1.18.0)", - "astropy (>=6.1.7,<8.0.0)", + "astropy (>=7.2.2,<9.0.0)", "matplotlib (>=3.10.8,<4.0.0)", "pooch (>=1.9.0,<2.0.0)", - "docutils (>=0.19,<1.0)", + "docutils (>=0.22,<1.0)", "httpxyz (>=0.31.2,<1.0)", "beautifulsoup4 (>=4.15.0,<5.0.0)", "lxml (>=6.1.1,<7.0.0)", @@ -34,10 +32,10 @@ dependencies = [ "tqdm (>=4.67.4,<5.0.0)", "packaging (>=26.0,<28.0)", - "synphot (>=1.6.1,<2.0.0)", - "skycalc-ipy (>=0.7.0,<1.0)", - "anisocado (>=0.5.0,<1.0)", - "astar-utils (>=0.6.0,<1.0)" + "synphot (>=1.7.0,<2.0.0)", + "skycalc-ipy (>=0.7.1,<1.0)", + "anisocado (>=0.6.0,<1.0)", + "astar-utils (>=0.7.0,<1.0)" ] [project.urls] @@ -72,7 +70,7 @@ ipykernel = "^7.3.0" optional = true [tool.poetry.group.docs.dependencies] -sphinx = "^8.1.3" +sphinx = "^9.1.0" sphinx-book-theme = "^1.1.4" jupyter-sphinx = ">=0.5.3" sphinx-copybutton = ">=0.5.2,<1.0" diff --git a/scopesim/reports/rst_utils.py b/scopesim/reports/rst_utils.py index b6e854b1c..c5e830ed2 100644 --- a/scopesim/reports/rst_utils.py +++ b/scopesim/reports/rst_utils.py @@ -314,7 +314,7 @@ def latexify_rst_text(rst_text, filename=None, path=None, title_char="=", text = "Title\n<<<<<\nSubtitle\n>>>>>>>>\n\n" parts = publish_parts( text + rst_text, - writer_name="latex", + writer="latex", # Settings_overrides to placate FutureWarnings. # TODO: Decide whether the future defaults look better. settings_overrides={ diff --git a/scopesim/utils.py b/scopesim/utils.py index c8db463d6..f81536864 100644 --- a/scopesim/utils.py +++ b/scopesim/utils.py @@ -715,7 +715,7 @@ def write_report(text, filename=None, output=None): for fmt in output: out_text = deepcopy(text) if fmt.lower() == "latex": - out_text = publish_string(out_text, writer_name="latex") + out_text = publish_string(out_text, writer="latex") out_text = out_text.decode("utf-8") suffix = {"rst": ".rst", "latex": ".tex"}[fmt] From 7b358d040a4e0f73b71eba3491685045d4a2b833 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Mon, 10 Aug 2026 21:14:30 +0200 Subject: [PATCH 33/68] Bump JupyterLab from 4.5.7 to 4.6.3 --- poetry.lock | 77 ++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/poetry.lock b/poetry.lock index ddc8210ad..4a17163ee 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1492,6 +1492,26 @@ jupyterlab = "*" nbconvert = "*" notebook = "*" +[[package]] +name = "jupyter-builder" +version = "1.2.2" +description = "JupyterLab build tools" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "jupyter_builder-1.2.2-py3-none-any.whl", hash = "sha256:6ebcd4c49daf5df6a18068a74a48010406700ed90a76c189fac43eaf85c60c63"}, + {file = "jupyter_builder-1.2.2.tar.gz", hash = "sha256:b6cea88f58e44b2c5eba96f28d2e0d16fd453d3ca6dc9c4492ff8a1f2e97f601"}, +] + +[package.dependencies] +jupyter-core = "*" +traitlets = "*" + +[package.extras] +dev = ["build", "hatch", "mypy", "pre-commit", "ruff (==0.16.0)"] +test = ["copier (>=9.3,<10)", "coverage[toml] (>=7.10.6)", "deptry", "jinja2-time", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-cov (>=7)"] + [[package]] name = "jupyter-cache" version = "1.0.0" @@ -1715,14 +1735,14 @@ test = ["bash-kernel", "pytest"] [[package]] name = "jupyterlab" -version = "4.5.7" +version = "4.6.3" description = "JupyterLab computational environment" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "jupyterlab-4.5.7-py3-none-any.whl", hash = "sha256:fba4cb0e2c44a52859669d8c98b45de029d5e515f8407bf8534d2a8fc5f0964d"}, - {file = "jupyterlab-4.5.7.tar.gz", hash = "sha256:55a9822c4754da305f41e113452c68383e214dcf96de760146af89ce5d5117b0"}, + {file = "jupyterlab-4.6.3-py3-none-any.whl", hash = "sha256:0a1ebc6567186f1eabd99536e94df7ed9e96d1e7c5ddf3e4406ae16e88abacb7"}, + {file = "jupyterlab-4.6.3.tar.gz", hash = "sha256:2e3db6e3a12495ebd188276e985bf5ac502fbde3d1e8628819920210008de498"}, ] [package.dependencies] @@ -1730,21 +1750,20 @@ async-lru = ">=1.0.0" httpx = ">=0.25.0,<1" ipykernel = ">=6.5.0,<6.30.0 || >6.30.0" jinja2 = ">=3.0.3" +jupyter-builder = ">=1.0.2" jupyter-core = "*" jupyter-lsp = ">=2.0.0" -jupyter-server = ">=2.4.0,<3" +jupyter-server = ">=2.19.0,<3" jupyterlab-server = ">=2.28.0,<3" notebook-shim = ">=0.2" -packaging = "*" -setuptools = ">=41.1.0" +packaging = ">=23.2" tornado = ">=6.2.0" traitlets = "*" [package.extras] -dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.11.12)"] -docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<8.2.0)", "sphinx-copybutton"] -docs-screenshots = ["altair (==6.0.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.5)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.3.post1)", "matplotlib (==3.10.0)", "nbconvert (>=7.0.0)", "pandas (==2.2.3)", "scipy (==1.15.1)"] -test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "requests", "requests-cache", "virtualenv"] +dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.15.15)", "shellcheck-py"] +docs-screenshots = ["altair (==6.0.0)", "ipykernel (<7.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.5)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.3.post1)", "matplotlib (==3.10.0)", "nbconvert (>=7.0.0)", "pandas (==2.2.3)", "scipy (==1.15.1)"] +test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "pytest-xdist", "requests", "requests-cache", "virtualenv"] upgrade-extension = ["copier (>=9,<10)", "jinja2-time (<0.3)", "pydantic (<3.0)", "pyyaml-include (<3.0)", "tomli-w (<2.0)"] [[package]] @@ -2518,19 +2537,20 @@ files = [ [[package]] name = "notebook" -version = "7.5.6" +version = "7.6.1" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "notebook-7.5.6-py3-none-any.whl", hash = "sha256:4dde3f8fb55fa8fb7946d58c6e869ce9baf46d00fc070664f62604569d0faca0"}, - {file = "notebook-7.5.6.tar.gz", hash = "sha256:621174aade80108f0020b0f00738000b215f75fa3cd90771ad7aa0f24536a4e1"}, + {file = "notebook-7.6.1-py3-none-any.whl", hash = "sha256:6ea1e4c926f0dc490444ddcc335797a3dacda470a805d01031872fb119988ba2"}, + {file = "notebook-7.6.1.tar.gz", hash = "sha256:0b45fd1010668dd4808c40914d957706dbf044a677d28764dc881b74dfcede82"}, ] [package.dependencies] -jupyter-server = ">=2.4.0,<3" -jupyterlab = ">=4.5.7,<4.6" +jupyter-builder = ">=1.0.2,<2" +jupyter-server = ">=2.19.0,<3" +jupyterlab = ">=4.6.2,<4.7" jupyterlab-server = ">=2.28.0,<3" notebook-shim = ">=0.2,<0.3" tornado = ">=6.2.0" @@ -2538,7 +2558,7 @@ tornado = ">=6.2.0" [package.extras] dev = ["hatch", "pre-commit"] docs = ["myst-parser", "nbsphinx", "pydata-sphinx-theme", "sphinx (>=1.3.6)", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["importlib-resources (>=5.0) ; python_version < \"3.10\"", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.28.0,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] +test = ["ipykernel", "jupyter-server[test] (>=2.19.0,<3)", "jupyterlab-server[test] (>=2.28.0,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] [[package]] name = "notebook-shim" @@ -3784,27 +3804,6 @@ nativelib = ["pyobjc-framework-Cocoa ; sys_platform == \"darwin\"", "pywin32 ; s objc = ["pyobjc-framework-Cocoa ; sys_platform == \"darwin\""] win32 = ["pywin32 ; sys_platform == \"win32\""] -[[package]] -name = "setuptools" -version = "78.1.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "setuptools-78.1.1-py3-none-any.whl", hash = "sha256:c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561"}, - {file = "setuptools-78.1.1.tar.gz", hash = "sha256:fcc17fd9cd898242f6b4adfaca46137a9edef687f43e6f78469692a5e70d851d"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] -core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] - [[package]] name = "six" version = "1.17.0" From cbdfea02a2f8f0a2fa49bfedb9de9afd84cf4fd3 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Mon, 10 Aug 2026 21:18:51 +0200 Subject: [PATCH 34/68] Only test supported versions --- .github/workflows/bump.yml | 12 ++++++++---- .github/workflows/bump_on_dependency.yml | 5 ++--- .github/workflows/notebooks_dispatch.yml | 6 +++--- .github/workflows/tests.yml | 8 ++++++++ 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/.github/workflows/bump.yml b/.github/workflows/bump.yml index beac85248..e71604e34 100644 --- a/.github/workflows/bump.yml +++ b/.github/workflows/bump.yml @@ -8,17 +8,21 @@ on: required: false rule: type: choice - description: Version bump level + description: Version bump level (ignored if explicit version is given) required: true options: - - prerelease - - "prerelease --next-phase" + - dev - prepatch - preminor - premajor - patch - minor - major + version: + type: string + description: Explicit version (overrides rule), e.g. "X.Y.Zb0" to enter feature freeze. + required: false + default: "" jobs: Bump: @@ -27,4 +31,4 @@ jobs: secrets: inherit with: dry-run: ${{ inputs.dry-run }} - rule: ${{ inputs.rule }} + rule: ${{ inputs.version != '' && inputs.version || inputs.rule }} diff --git a/.github/workflows/bump_on_dependency.yml b/.github/workflows/bump_on_dependency.yml index fa3bf51b3..974fd2d9f 100644 --- a/.github/workflows/bump_on_dependency.yml +++ b/.github/workflows/bump_on_dependency.yml @@ -5,7 +5,7 @@ on: jobs: Bump: - name: Bump prerelease version + name: Bump dev version if: > github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' && @@ -16,6 +16,5 @@ jobs: uses: AstarVienna/DevOps/.github/workflows/bump.yml@main secrets: inherit with: - rule: prerelease + rule: dev branch: main - diff --git a/.github/workflows/notebooks_dispatch.yml b/.github/workflows/notebooks_dispatch.yml index 1fac5689e..869c6daac 100644 --- a/.github/workflows/notebooks_dispatch.yml +++ b/.github/workflows/notebooks_dispatch.yml @@ -23,9 +23,9 @@ jobs: # is more useful than 5 green and one red. The others can be enabled # once we fix the problem properly. # os: [ubuntu-latest, windows-latest, macos-latest] - # python-version: ['3.8', '3.11'] + # python-version: ['3.12', '3.14'] os: [ubuntu-latest] - python-version: ["3.11"] + python-version: ["3.12"] runs-on: ${{ matrix.os }} steps: @@ -33,7 +33,7 @@ jobs: - name: Install poetry shell: bash - run: pipx install poetry==2.1.4 + run: pipx install poetry==2.4.1 - name: Set up Python uses: actions/setup-python@v7 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b1bc8e667..d2dfa5b67 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,16 +19,24 @@ jobs: name: Offline tests uses: AstarVienna/DevOps/.github/workflows/tests.yml@main secrets: inherit + with: + python-versions: '["3.12", "3.13", "3.14"]' + python-min: "3.12" # limited versions for macOS only + python-max: "3.14" # limited versions for macOS only call-webtests: name: Network tests uses: AstarVienna/DevOps/.github/workflows/webtests.yml@main secrets: inherit + with: + python-versions: '["3.12", "3.14"]' call-updated-tests: name: Tests with updated dependencies uses: AstarVienna/DevOps/.github/workflows/updated_tests.yml@main secrets: inherit + with: + python-versions: '["3.12", "3.14"]' call-notebooks-with-clone: name: Notebook tests From d74a44644782bc9089ab973e4e12aaa039ea8ae6 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Mon, 10 Aug 2026 21:19:14 +0200 Subject: [PATCH 35/68] Bump Ubuntu and Python in RTD config --- .readthedocs.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 389c1d7a1..4a10c86b7 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -5,9 +5,9 @@ version: 2 build: - os: "ubuntu-24.04" + os: "ubuntu-26.04" tools: - python: "3.11" + python: "3.14" jobs: post_checkout: - git clone -n --depth=1 --filter=tree:0 https://github.com/AstarVienna/irdb ./docs/source/examples/inst_pkgs From 5b854254e3f8a15c7e97674c24bfd1cfcc9e667b Mon Sep 17 00:00:00 2001 From: "astarvienna-bot[bot]" <217837468+myciapp[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 10:06:20 +0000 Subject: [PATCH 36/68] Bumping version from 0.12.0b2.dev0 to 0.12.0b2.dev1 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d2a90ee75..142d75f35 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ScopeSim" -version = "0.12.0b2.dev0" +version = "0.12.0b2.dev1" description = "Generalised telescope observation simulator" license = {text = "GPL-3.0-or-later"} authors = [ From b7f5db677f5bc71e7e822187010ab06a7920f9f2 Mon Sep 17 00:00:00 2001 From: oczoske Date: Tue, 11 Aug 2026 18:12:28 +0200 Subject: [PATCH 37/68] Make laser parameters configurable; utility function for tunable laser --- scopesim/effects/metis_wcu/metis_wcu.py | 60 ++++++++++++++++--- scopesim/tests/tests_effects/test_MetisWCU.py | 19 +++++- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/scopesim/effects/metis_wcu/metis_wcu.py b/scopesim/effects/metis_wcu/metis_wcu.py index 04e589af9..138b2cd56 100644 --- a/scopesim/effects/metis_wcu/metis_wcu.py +++ b/scopesim/effects/metis_wcu/metis_wcu.py @@ -200,6 +200,50 @@ def set_lamp(self, lamp="bb"): self.compute_lamp_emission() self.compute_fp_emission() + def tune_laser(self, wavelength, force=False): + """Set the wavelength(s) of the tunable laser + + Parameters + ========== + wavelength : float, list, array [um] + The wavelength(s) at which the laser emits + force: boolean [True] <- currently not used + If True bypass the validation for the lasers available + wavelength range. + """ + if not hasattr(wavelength, "__len__"): + wavelength = [wavelength] + if not force: + wavelength = self._validate_tunable(wavelength) + + # if nothing's there use a dummy value outside any range + # to prevent division by zero + if len(wavelength) == 0: + wavelength = [0.001] + + self.meta["laser_t_wave"] = wavelength + self.compute_lamp_emission() + self.compute_fp_emission() + + def _validate_tunable(self, wave): + """Validate wave against a wavelength range + + This is currently not used as there is no reliable + information on the tuning range of the QCL. + """ + reject = [] + accept = [] + lam_min = 1. + lam_max = 30. + for lam in wave: + if lam < lam_min or lam > lam_max: + reject.append(lam) + else: + accept.append(lam) + if len(reject) > 0: + logger.warning("Removed %d wavelengths outside allowed range (%f, %f) um", len(reject), lam_min, lam_max) + return accept + def get_wavelength(self): """Try to set the appropriate wavelength vector for mode and filter.""" # Need to provide for wcu_lms_extended @@ -423,24 +467,26 @@ def _laser_intensity(self): The function computes all three lasers at once. This is possible because the lines are so far apart that there is no band that sees them both. """ - print("Computing laser intensity") + logger.info("Computing laser intensity") lam = self.wavelength dlam = lam[1] - lam[0] mult_is = self.is_multiplication(lam) # Laser 1 (L band) # TODO move to yaml - lamc_l = 3.39 * u.um - power_l = 5e-3 * u.W / (c.c * c.h / lamc_l) * u.ph + lamc_l = self.meta["laser_l_wave"] * u.um + power_l = self.meta["laser_l_power"] * u.W / (c.c * c.h / lamc_l) * u.ph # Laser 2 (tunable), power divided among multiple lines - lam_t = seq(4.68, 4.78, 0.01) * u.um + if not hasattr(self.meta['laser_t_wave'], "__len__"): + self.meta['laser_t_wave'] = [self.meta['laser_t_wave']] + lam_t = self.meta["laser_t_wave"] * u.um nline = len(lam_t) - power_t = 70e-3 * u.W / (c.c * c.h / lam) * u.ph / nline + power_t = self.meta["laser_t_power"] * u.W / (c.c * c.h / lam) * u.ph / nline # Laser 3 (M band) - lamc_m = 5.26 * u.um - power_m = 20e-3 * u.W / (c.c * c.h / lamc_m) * u.ph + lamc_m = self.meta["laser_m_wave"] * u.um + power_m = self.meta["laser_m_power"] * u.W / (c.c * c.h / lamc_m) * u.ph # Apply fibre transmission power_l *= self.fibre_trans diff --git a/scopesim/tests/tests_effects/test_MetisWCU.py b/scopesim/tests/tests_effects/test_MetisWCU.py index 5261e4065..2967a9879 100644 --- a/scopesim/tests/tests_effects/test_MetisWCU.py +++ b/scopesim/tests/tests_effects/test_MetisWCU.py @@ -40,6 +40,12 @@ def fixture_bbsource(): bb_temp=1000*u.K, is_temp=300*u.K, wcu_temp=300*u.K, + laser_l_wave=3.39, # [um] + laser_l_power=5e-3, # [W] + laser_t_wave=[4.70], # [um], array + laser_t_power=70e-3, # [W] + laser_m_wave=5.26, # [um] + laser_m_power=20e-3, # [W] bb_aperture=1., bb_to_is=None, rho_tube=0.95, @@ -211,7 +217,18 @@ def test_laser_brighter_with_fibre_transmission(self, bbsource): bbsource.set_lamp("laser") intens_2 = np.sum(bbsource.intens_lamp) - assert intens_2 / intens_1 == ft_2 / ft_1 + assert np.allclose(intens_2 / intens_1, ft_2 / ft_1) + + + def test_can_tune_laser(self, bbsource): + bbsource.set_lamp("laser") + assert bbsource.meta["laser_t_wave"] == [4.70] + bbsource.tune_laser(4.72) + assert bbsource.meta["laser_t_wave"] == [4.72] + bbsource.tune_laser(np.linspace(4.68, 4.72, 10)) + assert len(bbsource.meta["laser_t_wave"]) == 10 + bbsource.tune_laser([]) + assert bbsource.meta["laser_t_wave"] == [0.001] @pytest.fixture(name="fpmask", scope="function") From cfd0a7ed67feaf23da27885df33f0943239133d8 Mon Sep 17 00:00:00 2001 From: "Fabian H." <73600109+teutoburg@users.noreply.github.com> Date: Wed, 12 Aug 2026 13:50:25 +0200 Subject: [PATCH 38/68] Add prepare-release workflow from elsewhere --- .github/workflows/prepare_release.yml | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/prepare_release.yml diff --git a/.github/workflows/prepare_release.yml b/.github/workflows/prepare_release.yml new file mode 100644 index 000000000..705405e4f --- /dev/null +++ b/.github/workflows/prepare_release.yml @@ -0,0 +1,47 @@ +name: Prepare release +on: + workflow_dispatch: + inputs: + rule: + type: choice + description: Version bump level for the release (ignored if explicit version is given) + required: true + options: + - patch + - minor + - major + - prepatch + - preminor + - premajor + version: + type: string + description: > + Explicit release version (overrides rule), e.g. "X.Y.Zb0" to tag the feature freeze. + required: false + default: "" + notes: + type: string + description: > + Optional manual release notes section, shown above the + auto-generated notes. Can also be added or edited in the + draft release before publishing. + required: false + default: "" + previous-tag: + type: string + description: > + Optional comparison base for auto-generated notes. Only needed + for backport releases from maintenance branches (e.g. "v2.1.0" + when releasing 2.1.1 while 2.5.0 already exists). + required: false + default: "" + +jobs: + Prepare: + name: Prepare release + uses: AstarVienna/DevOps/.github/workflows/prepare_release.yml@main + secrets: inherit + with: + rule: ${{ inputs.version != '' && inputs.version || inputs.rule }} + notes: ${{ inputs.notes }} + previous-tag: ${{ inputs.previous-tag }} From 4e60fecb73dc3f56b030ea2f49f03849b317a873 Mon Sep 17 00:00:00 2001 From: oczoske Date: Wed, 12 Aug 2026 16:23:30 +0200 Subject: [PATCH 39/68] apply review comments --- scopesim/effects/metis_wcu/metis_wcu.py | 23 ++++++++----------- scopesim/tests/tests_effects/test_MetisWCU.py | 1 + 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/scopesim/effects/metis_wcu/metis_wcu.py b/scopesim/effects/metis_wcu/metis_wcu.py index 138b2cd56..7f8659bc8 100644 --- a/scopesim/effects/metis_wcu/metis_wcu.py +++ b/scopesim/effects/metis_wcu/metis_wcu.py @@ -6,6 +6,7 @@ """ from typing import ClassVar +from collections.abc import Iterable import numpy as np from astropy.table import Table @@ -211,7 +212,7 @@ def tune_laser(self, wavelength, force=False): If True bypass the validation for the lasers available wavelength range. """ - if not hasattr(wavelength, "__len__"): + if not isinstance(wavelength, Iterable): wavelength = [wavelength] if not force: wavelength = self._validate_tunable(wavelength) @@ -231,17 +232,13 @@ def _validate_tunable(self, wave): This is currently not used as there is no reliable information on the tuning range of the QCL. """ - reject = [] - accept = [] - lam_min = 1. - lam_max = 30. - for lam in wave: - if lam < lam_min or lam > lam_max: - reject.append(lam) - else: - accept.append(lam) - if len(reject) > 0: - logger.warning("Removed %d wavelengths outside allowed range (%f, %f) um", len(reject), lam_min, lam_max) + lam_min, lam_max = self.meta["laser_t_wave_limits"] + + accept = [w for w in wave if (w >= lam_min) & (w <= lam_max)] + n_rejected = len(wave) - len(accept) + if n_rejected > 0: + logger.warning("Removed %d wavelengths outside allowed range (%.2f, %.2f) um", + n_rejected, lam_min, lam_max) return accept def get_wavelength(self): @@ -478,7 +475,7 @@ def _laser_intensity(self): power_l = self.meta["laser_l_power"] * u.W / (c.c * c.h / lamc_l) * u.ph # Laser 2 (tunable), power divided among multiple lines - if not hasattr(self.meta['laser_t_wave'], "__len__"): + if not isinstance(self.meta['laser_t_wave'], Iterable): self.meta['laser_t_wave'] = [self.meta['laser_t_wave']] lam_t = self.meta["laser_t_wave"] * u.um nline = len(lam_t) diff --git a/scopesim/tests/tests_effects/test_MetisWCU.py b/scopesim/tests/tests_effects/test_MetisWCU.py index 2967a9879..c53257472 100644 --- a/scopesim/tests/tests_effects/test_MetisWCU.py +++ b/scopesim/tests/tests_effects/test_MetisWCU.py @@ -43,6 +43,7 @@ def fixture_bbsource(): laser_l_wave=3.39, # [um] laser_l_power=5e-3, # [W] laser_t_wave=[4.70], # [um], array + laser_t_wave_limits=[1., 30.], # [um] laser_t_power=70e-3, # [W] laser_m_wave=5.26, # [um] laser_m_power=20e-3, # [W] From 9684627a8543d1f8d9131cb8c77b99fda2bc3335 Mon Sep 17 00:00:00 2001 From: oczoske Date: Wed, 12 Aug 2026 16:47:40 +0200 Subject: [PATCH 40/68] one more --- scopesim/tests/tests_effects/test_MetisWCU.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scopesim/tests/tests_effects/test_MetisWCU.py b/scopesim/tests/tests_effects/test_MetisWCU.py index c53257472..8c8f63d47 100644 --- a/scopesim/tests/tests_effects/test_MetisWCU.py +++ b/scopesim/tests/tests_effects/test_MetisWCU.py @@ -218,7 +218,7 @@ def test_laser_brighter_with_fibre_transmission(self, bbsource): bbsource.set_lamp("laser") intens_2 = np.sum(bbsource.intens_lamp) - assert np.allclose(intens_2 / intens_1, ft_2 / ft_1) + npt.assert_allclose(intens_2 / intens_1, ft_2 / ft_1) def test_can_tune_laser(self, bbsource): From 2d73d5e846baf1af33e48b4e449de3dfeee7a04e Mon Sep 17 00:00:00 2001 From: oczoske Date: Thu, 13 Aug 2026 18:26:37 +0200 Subject: [PATCH 41/68] AtmoLibraryTERCurve works with parameters other than pwv --- scopesim/effects/ter_curves.py | 16 ++++++++++------ .../tests_effects/test_AtmoLibraryTERCurve.py | 11 +++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/scopesim/effects/ter_curves.py b/scopesim/effects/ter_curves.py index c653fd73c..c0721e85b 100644 --- a/scopesim/effects/ter_curves.py +++ b/scopesim/effects/ter_curves.py @@ -286,7 +286,8 @@ class AtmoLibraryTERCurve(AtmosphericTERCurve): extension 3 etc. Tables with columns `transmission` and `emission`. - Currently the curves are distinguished by a single parameter (`pwv`). + Currently the curves are distinguished by a single parameter, whose name + is specified by `parameter` (cf. the examples below). .. versionadded:: 0.11.1 @@ -298,12 +299,14 @@ class AtmoLibraryTERCurve(AtmosphericTERCurve): class: AtmoLibraryTERCurve kwargs: filename: "!ATMO.spectrum.filename + parameter: "pwv" pwv: 10. - name: atmosphere class: AtmoLibraryTERCurve kwargs: - pwv: 25. + parameter: "relH" + relH: 25 remote_filename: "!ATMO.spectrum.filename" The location of a downloaded file is provided by `.meta['filename']`. @@ -318,9 +321,9 @@ def __init__(self, cmds=None, **kwargs): remote_filename = from_currsys(kwargs["remote_filename"], cmds) kwargs["filename"] = self._download_library(remote_filename) - self.param = 'pwv' super().__init__(cmds=cmds, **kwargs) self.meta.update(kwargs) + self.param = self.meta['parameter'] self.load_table_from_library() def update(self, **kwargs): @@ -343,8 +346,7 @@ def _download_library(fname: str) -> str: def load_table_from_library(self): """Load the appropriate library extension based on parameter value.""" - param = 'pwv' - + param = self.param self.value = from_currsys(self.meta[param], self.cmds) self.ext_data = self._file[0].header["EDATA"] self.ext_cat = self._file[0].header["ECAT"] @@ -367,10 +369,12 @@ def load_table_from_library(self): tbl.add_column(wavelength, index=0) tbl.meta["wavelength_unit"] = tbl["wavelength"].unit - tbl.meta["emission_unit"] = tbl["emission"].unit + if "emission" in tbl.colnames: + tbl.meta["emission_unit"] = tbl["emission"].unit self.surface.table = tbl self.surface.meta.update(tbl.meta) + self.meta.update(tbl.meta) def __str__(self) -> str: """Return str(self).""" diff --git a/scopesim/tests/tests_effects/test_AtmoLibraryTERCurve.py b/scopesim/tests/tests_effects/test_AtmoLibraryTERCurve.py index 0ffbbea15..3d2f9a178 100644 --- a/scopesim/tests/tests_effects/test_AtmoLibraryTERCurve.py +++ b/scopesim/tests/tests_effects/test_AtmoLibraryTERCurve.py @@ -15,9 +15,17 @@ class TestLocalFile: def test_initialises_from_local_file(self, mock_path): with patch("scopesim.rc.__search_path__", [mock_path]): atmo = AtmoLibraryTERCurve(filename=MOCKFILE, + parameter="pwv", pwv=1) assert isinstance(atmo, AtmoLibraryTERCurve) + def test_fails_with_wrong_parameter(self, mock_path): + with patch("scopesim.rc.__search_path__", [mock_path]): + with pytest.raises(KeyError): + _ = AtmoLibraryTERCurve(filename=MOCKFILE, + parameter="relH", + relH=50) + @pytest.mark.parametrize("pwv, extname", [(1.0, "PWV_01"), (23, "PWV_23"), @@ -26,6 +34,7 @@ def test_initialises_from_local_file(self, mock_path): def test_picks_nearest_pwv(self, pwv, extname, mock_path): with patch("scopesim.rc.__search_path__", [mock_path]): atmo = AtmoLibraryTERCurve(filename=MOCKFILE, + parameter="pwv", pwv=pwv) assert atmo.meta['extname'] == extname assert atmo.meta['pwv'] == pwv @@ -34,6 +43,7 @@ def test_updates_with_new_pwv(self, mock_path): with patch("scopesim.rc.__search_path__", [mock_path]): oldpwv, newpwv = 1, 22.5 atmo = AtmoLibraryTERCurve(filename=MOCKFILE, + parameter="pwv", pwv=oldpwv) assert atmo.meta['extname'] == "PWV_01" assert atmo.meta['pwv'] == oldpwv @@ -45,6 +55,7 @@ def test_update_with_unknown_parameter_issues_warning(self, mock_path, caplog): caplog.set_level(logging.WARNING) with patch("scopesim.rc.__search_path__", [mock_path]): atmo = AtmoLibraryTERCurve(filename=MOCKFILE, + parameter="pwv", pwv=1.) atmo.update(temp=23) assert "Can only update with parameter pwv" in caplog.text From cc19b8f52baea68cbf56badabfb4f25cec819363 Mon Sep 17 00:00:00 2001 From: Kieran Leschinski Date: Fri, 14 Aug 2026 08:49:20 +0200 Subject: [PATCH 42/68] Skip PSF convolution for exactly-uniform fields PSF.apply_to subtracts the background level before convolving and re-adds it afterwards, so for a spatially uniform field the convolution input is identically zero and the operation provably cannot change the image. Yet the kernel interpolation/rescale and the padded-grid FFT were still executed - for METIS this is the dominant cost of every dark and closed-shutter calibration frame (measured: ~57 s of a 75 s img_lm dark, i.e. 76% of the frame, spent convolving a field the operation cannot change; the IFU pays the same pattern on (n_wave, x, y) cubes, where the padded FFT copies have also been observed to exhaust memory). Check the residual (image minus background) before preparing the kernel and skip the convolution when it is exactly zero everywhere. The check is exact, not a tolerance: fields with any structure - including micro-ADU thermal gradients - take the unchanged full path, so results are bit-for-bit identical either way. The FovVolumeList waveset handling above is untouched, so FOV chunking does not change. The only reordering is computing image/bkg_level before get_kernel instead of after; neither depends on the kernel. Co-Authored-By: Claude Fable 5 --- scopesim/effects/psfs/psf_base.py | 32 ++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/scopesim/effects/psfs/psf_base.py b/scopesim/effects/psfs/psf_base.py index 11a1b7cc3..1912d66cf 100644 --- a/scopesim/effects/psfs/psf_base.py +++ b/scopesim/effects/psfs/psf_base.py @@ -72,6 +72,33 @@ def apply_to(self, obj, **kwargs): logger.debug("Executing %s, convolution", self.meta['name']) if ((hasattr(obj, "fields") and len(obj.fields) > 0) or (obj.hdu is not None)): + image = obj.hdu.data.astype(float) + + # subtract background level before convolving, re-add afterwards + bkg_level = get_bkg_level(image, self.meta["bkg_width"]) + + # Short-circuit for spatially uniform fields (e.g. darks and + # closed-shutter calibration frames): the background is + # subtracted before the convolution below and re-added after, + # so for a uniform image the convolution input is identically + # zero and the output is bit-for-bit the input. Skip the + # (expensive) kernel interpolation/rescale and FFT entirely. + # This is exact, not approximate: it only fires when the + # residual is exactly zero everywhere. + # np.asarray: the FOV data can be an astropy Quantity, whose + # truthiness/.any() raises TypeError; we only need the values + if image.ndim == 3: + residual = np.asarray( + image - np.asarray(bkg_level)[:, None, None]) + else: + residual = np.asarray(image - bkg_level) + if not residual.any(): + logger.debug( + "%s: uniform field, convolution skipped", + self.meta["name"]) + obj.hdu.data = image + return obj + kernel = self.get_kernel(obj).astype(float) # This doesn't work because of a "Delta PSF" in some mocks... @@ -94,11 +121,6 @@ def apply_to(self, obj, **kwargs): # kernel /= np.sum(kernel) # kernel[kernel < 0.] = 0. - image = obj.hdu.data.astype(float) - - # subtract background level before convolving, re-add afterwards - bkg_level = get_bkg_level(image, self.meta["bkg_width"]) - # do the convolution mode = from_currsys(self.meta["convolve_mode"], self.cmds) From d3bbb28f5a4d0c6d35f2cd79f47114f64619429c Mon Sep 17 00:00:00 2001 From: oczoske Date: Mon, 17 Aug 2026 10:25:51 +0200 Subject: [PATCH 43/68] Registry entry for MICADO calibration transmission --- scopesim/server/atmo_registry.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/scopesim/server/atmo_registry.txt b/scopesim/server/atmo_registry.txt index 4fb92d451..d5468f1c4 100644 --- a/scopesim/server/atmo_registry.txt +++ b/scopesim/server/atmo_registry.txt @@ -1 +1,2 @@ Leiden_atmo_ter.fits 16b5faa1bc4e75ba2d34cad02f302223b8a788306cd34b9809c3e895b46132b0 +MICADO_water_trans.fits e7bdd8bdd983aa7e562dfe8e2664cc554d8e08c1dbed6aedca39f32f38e5cf8c From b5172c85376d7a63b4146dda404966613c7588aa Mon Sep 17 00:00:00 2001 From: oczoske Date: Tue, 18 Aug 2026 18:32:01 +0200 Subject: [PATCH 44/68] relH as float --- scopesim/effects/ter_curves.py | 2 +- scopesim/server/atmo_registry.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scopesim/effects/ter_curves.py b/scopesim/effects/ter_curves.py index c0721e85b..4d5e1f8b3 100644 --- a/scopesim/effects/ter_curves.py +++ b/scopesim/effects/ter_curves.py @@ -306,7 +306,7 @@ class AtmoLibraryTERCurve(AtmosphericTERCurve): class: AtmoLibraryTERCurve kwargs: parameter: "relH" - relH: 25 + relH: 0.25 remote_filename: "!ATMO.spectrum.filename" The location of a downloaded file is provided by `.meta['filename']`. diff --git a/scopesim/server/atmo_registry.txt b/scopesim/server/atmo_registry.txt index d5468f1c4..77ae643d1 100644 --- a/scopesim/server/atmo_registry.txt +++ b/scopesim/server/atmo_registry.txt @@ -1,2 +1,2 @@ Leiden_atmo_ter.fits 16b5faa1bc4e75ba2d34cad02f302223b8a788306cd34b9809c3e895b46132b0 -MICADO_water_trans.fits e7bdd8bdd983aa7e562dfe8e2664cc554d8e08c1dbed6aedca39f32f38e5cf8c +MICADO_relay_air_trans.fits bea89caf7b6690790c409f7ed518b7e0783ce98098ac1b7343025b2d9173b04b From 93f32cb44c47acc6053518325a399ad5c65fbc50 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Thu, 20 Aug 2026 14:41:31 +0200 Subject: [PATCH 45/68] Bump matplotlib and skycalc-ipy --- poetry.lock | 187 +++++++++++++++---------------------------------- pyproject.toml | 4 +- 2 files changed, 59 insertions(+), 132 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4a17163ee..3cd37f028 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2218,64 +2218,64 @@ files = [ [[package]] name = "matplotlib" -version = "3.11.0" +version = "3.11.1" description = "Python plotting package" optional = false python-versions = ">=3.11" groups = ["main", "dev", "docs"] files = [ - {file = "matplotlib-3.11.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f857524b442f0f36e641868ce2171aafa88cb0bc0644f4e1d8a5df9b32649fef"}, - {file = "matplotlib-3.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:57baa92fdc82948ed716eae6d2579d4d6f40965cd8d2f416755b4a72580a3233"}, - {file = "matplotlib-3.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:630eee0e67d35cce2019a0e670719f4816e3b86aff0fa72729f6c69786fceb45"}, - {file = "matplotlib-3.11.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5106c444d0bf966eee2853548c03772af4ab7199118e086c62fbac8ccb07c055"}, - {file = "matplotlib-3.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d7aea652b58e686444079be3376ef546bffa1eee9b9bb9c472b9fcf6cf410d3"}, - {file = "matplotlib-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:70a5b3e9a5dab708c0f039709ae7c68d5b4d254e291ef76492cdba230c8bb5e4"}, - {file = "matplotlib-3.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:3d68266213e73823ac3be90615bab0cf31f88851e114cdb1dd25dacf3b01e1a7"}, - {file = "matplotlib-3.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06b5872e9cf11adc8f589ded3ce11bc3e1061ad498259664fabc1f6615beb918"}, - {file = "matplotlib-3.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0515d495124be3124340e59f164d901ed4484e2246a5b74cfa483cac3b80bd97"}, - {file = "matplotlib-3.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be5f93a1d21981bfb802ded0d77a0caa92d4342a47d45754fac77e314a506344"}, - {file = "matplotlib-3.11.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41635d7909d19e52e924a521dde6d8f670b0f53ab1d0e8c331fa831554f681d1"}, - {file = "matplotlib-3.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:94f5000f67ca9faa300863ea17f8bce9175cb67b88bec4bc7780502d53dd7c9e"}, - {file = "matplotlib-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ac6f1ef39f3d0f9e2463303013094992cdbe0f85f43bc54155bc472b2042768e"}, - {file = "matplotlib-3.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:9dd11fb612ce7bc60b1de5b4fc87ff959d22317b5de42aabf392f66f97af22eb"}, - {file = "matplotlib-3.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce3b839b34ae1f430b4616893a2945a2999debaa7e94e7e29a2a8bbf286f7b5"}, - {file = "matplotlib-3.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:373db8f91214e8ccaf35ac833cc1dd59dd961e148bbd55dd027141591dde1313"}, - {file = "matplotlib-3.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be152b7570324dc8d01574cc9474dd2d803237acf528bcbb5b211fa347461a09"}, - {file = "matplotlib-3.11.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:126f256df600652d7e4b394cf3164ff75210a00038f287c95a012a6f58d0e83f"}, - {file = "matplotlib-3.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:03acfeddf87b0dddb11b081ef7740ad445a3ca8bcb6b8e3011b08f2cf802b75c"}, - {file = "matplotlib-3.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:ab3722f04f3ff34c23b5012c5873d2894174e06c3822fcdac3610965a5ac7d06"}, - {file = "matplotlib-3.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:c945824670fb8915b4ac879e5e61f3c58e0913022f70a0de4c082b17372f8771"}, - {file = "matplotlib-3.11.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3489c3dc487669b4a980bc3068f87856de7a1564248d3f6c629efb2a58b03f24"}, - {file = "matplotlib-3.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6a98f5476ce784a50ce09998f4ae1e6a9f25043cef8a480c98949902eda74620"}, - {file = "matplotlib-3.11.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:565af866fd63e4bd3f987d580afe27c44c2552a3b3305f4ecbb85133601ea6f3"}, - {file = "matplotlib-3.11.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6b3e64dea5062c570f04358e2711859f3531b459f29516274fbad889079e4f3"}, - {file = "matplotlib-3.11.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:942b37c5db1899610bd1543ce8e13e4ecff9a4633e7f63bb6aa9205d2644ebd1"}, - {file = "matplotlib-3.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c08e649a6313e1291e713623b97a38e5bb4aa580b2a100a94a3309bc6b9c8eb3"}, - {file = "matplotlib-3.11.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2746cd2c113742ff6ce37a864c5ac5fd7aa644568f445e66166e457ac78e40e0"}, - {file = "matplotlib-3.11.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3338e3e3de128cf50d0d2fb92a122815daf9c755bd882a474343c05f8fd7ec79"}, - {file = "matplotlib-3.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:25c2e5455efd8d99f41fb79871a31feb7d301569642e332ec58d72cfe9282bc3"}, - {file = "matplotlib-3.11.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9695457a467ff86d23f35037a43deb6f1134dd6d3e2ac8ce1e2087cff09ffb9"}, - {file = "matplotlib-3.11.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19c16c61dea63b3582918503e6b294193961261d9daa806d4ae2151f1ad05430"}, - {file = "matplotlib-3.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2d72ea8b7924f3cb955e61518d21e43b3df1e6c8a793b480a0c1214f185d30ba"}, - {file = "matplotlib-3.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:1c02da0a629dfa9debf52725ea06866b74c1fb70a895bae05e4493d34074f9f2"}, - {file = "matplotlib-3.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aa55d73b3117d4b07f959cd9eb6f69b375d8df3414139c479388e551aa5d999d"}, - {file = "matplotlib-3.11.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a9d8c6e7cd2f0ddf11d8d92e520dd1d9d2abb0cf6ac8831e338666c81e905847"}, - {file = "matplotlib-3.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:be050fcf32f729eda99f7f75a80bf67612ce16ab9ac1c23a387dcaede95cb70e"}, - {file = "matplotlib-3.11.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dfabef0230d0697aa0d717385194dd41162e00207a68bf4abf94c2bf4c27dca0"}, - {file = "matplotlib-3.11.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1644db30e759199443493ac5e5caec24fdb775a8f6123021f85ba47c4133c3cb"}, - {file = "matplotlib-3.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:15b0d160079cb10699a0e98b5989c70677b2df7cacdc62af67c30f2facec46d9"}, - {file = "matplotlib-3.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:446307e6b04b57b1f1239e228a1ec2af0d589a1008cebc3dfa3f5441d095cfb6"}, - {file = "matplotlib-3.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:652fb5696271d4c50f196d22a5ff4f8e4444c74f847423570d7dc0aa2bbd0159"}, - {file = "matplotlib-3.11.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:81ae77077a1e16d37a5b61096ccb07c8d90a99b518fa8256b8f21578932f2f62"}, - {file = "matplotlib-3.11.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ddef37840695f5eef65f9f070fe2d2f510f584c2156203f9f622a5b0584efffd"}, - {file = "matplotlib-3.11.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf662e5ac5707658cb931e19972c4bd99f7b4f8b7bf79d3c821d239fa6b71e64"}, - {file = "matplotlib-3.11.0.tar.gz", hash = "sha256:68c0c7be01b30dcca3638934f7f591df73401235cbdbf0d1ab1c71e7db7f8b57"}, + {file = "matplotlib-3.11.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b7cf158e7add54a8d51ac9b5a84abd6d4e13ed4951b4f25f1c5139f41c2addb2"}, + {file = "matplotlib-3.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d2ace7273b9a5061a3b420918a16fae1f2dc5dfee1abcc13aba71b5d94b1820c"}, + {file = "matplotlib-3.11.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee55e9041211bf84302ab55ec3965df18dd90ae19f8b58332a7feaf208bfe83"}, + {file = "matplotlib-3.11.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f4bdeea33a8d15a071dbfe6d119451b1d719c733ac666d65357082901a9099"}, + {file = "matplotlib-3.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b4c78ceb2f11bcac7389d305cda17aeb1f4586a857854ab5780bd3dd8dbfc407"}, + {file = "matplotlib-3.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:7f33a781e12b1e53b278deb2f5373c2e55ec4f10727be3440c0cfb5cda9f944f"}, + {file = "matplotlib-3.11.1-cp311-cp311-win_arm64.whl", hash = "sha256:67e4c3cd578c65ebd81bdc09a1b6592ceafee6dfafe116dc85dfcb647b5bbb18"}, + {file = "matplotlib-3.11.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e15ef41507f3d525f46154ac9e3ae785dacde9f20e593a25de8986267892ef74"}, + {file = "matplotlib-3.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:21a67b961a6d597bca54fae826cd20695ba4a6e4d05424a08da6e13e3176fd6b"}, + {file = "matplotlib-3.11.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba8f811b8ddfac493734d6af0b2dff96919d0c28ca0d641858dab4262777c6ea"}, + {file = "matplotlib-3.11.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c52f7ad20ef476806ed212380b1d54d20310c8b86bdc2c9a68b51f0024a44472"}, + {file = "matplotlib-3.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8b14eb22961fe865efb0e4ff167e333e428908b00115a8d800ccb65ee108e481"}, + {file = "matplotlib-3.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:88a2a27dd9691ae448dfae4b26f59036be90c3c28757edd3553a29559d00859f"}, + {file = "matplotlib-3.11.1-cp312-cp312-win_arm64.whl", hash = "sha256:480194afceca4df2f137c2721227d3cba67121fbf4397b69cee7f83714b0a58a"}, + {file = "matplotlib-3.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6771b0cd7838c6a857a7209814158c0ad09bfef878db3033dd82d70ad101f191"}, + {file = "matplotlib-3.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2abdee5ffa2fe11b2d19f7a5c63b785fb7c28cc46c7bc1814156341d9d1a33e1"}, + {file = "matplotlib-3.11.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0a19dcf73406d3746d25a5ed42d713604c9a3e024d129b102852b0d941cb9f3"}, + {file = "matplotlib-3.11.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7389b77ed2ab0552f46d9a90b81b7b8e6dfcdc42adc36c37a0865799843e0e3e"}, + {file = "matplotlib-3.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c90be0b73568da4f662afac580956a76e308437e641b4a45aa08925eeb67d95f"}, + {file = "matplotlib-3.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:68408341f2312836fbbdf6b3c78047f65b2d8752f5fd221c3e72d348f5b34f8b"}, + {file = "matplotlib-3.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c1f44890d435c1b4ef52f701ad5828cb450ea97bcc83918fda6be74965d6cd2"}, + {file = "matplotlib-3.11.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:5e510088c27a89d53580a752f959146893563e63c330e161d159b0fee652af6f"}, + {file = "matplotlib-3.11.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:1524e2bdd48a93557aa47ddcfe9c225dfdd57d5a01a5c49128c20f0632980ee1"}, + {file = "matplotlib-3.11.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:11664c551345553db92e61cae6cf1376f138f8c47cafdf13b64b18f3e3e9e464"}, + {file = "matplotlib-3.11.1-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e1f8922ba31959cf6a9dfb51be64b7f7bc582801a3957dc0c2f3afcd3537adf"}, + {file = "matplotlib-3.11.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83235693abde86e5e0129998f80ee39fc7f58e6d56a88fafb28a9278833e9d5f"}, + {file = "matplotlib-3.11.1-cp313-cp313t-win_amd64.whl", hash = "sha256:9a076f4fc5cdc43fdf510f5981418d25c2db4973418d9f22d8bb3dc8045ada78"}, + {file = "matplotlib-3.11.1-cp313-cp313t-win_arm64.whl", hash = "sha256:216fbb93a74add02ddb4cb38ef5348f59ac00b3e84567eaf16598772d40e150a"}, + {file = "matplotlib-3.11.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:30c492d4ba9448595b6fd8708c6725963f8148e25c0d8842948da5b05f0ee8d3"}, + {file = "matplotlib-3.11.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ac104be2768ffdd8655db9e71b768cbb45f2b9aa7b450cf1595e8f65d3822319"}, + {file = "matplotlib-3.11.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be943cb68bc6660ead58c55b3aa6366cba2ef7feb06460fbcce32360376f19f"}, + {file = "matplotlib-3.11.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5af0dcda57d471440a7b5b623e70e0a61003518443d9098f211a96ecfbbc25be"}, + {file = "matplotlib-3.11.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3d3fd84082b1afbd9398466c81309e20045be20d48fe0fb18c43504d164cbbb2"}, + {file = "matplotlib-3.11.1-cp314-cp314-win_amd64.whl", hash = "sha256:9601a1e90be21e4884c53b4f3dc3ee0544654946f9975258d691f1c2e2f119c6"}, + {file = "matplotlib-3.11.1-cp314-cp314-win_arm64.whl", hash = "sha256:ae30c6109848ac0f9fa36c5d6270938487614c47ba31860bd5361266dabc5685"}, + {file = "matplotlib-3.11.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:dadfe80797174e2984aae3be0b77594a3c72d2c0a40fbd4a0de48d2728caf3ae"}, + {file = "matplotlib-3.11.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:89b193b255f4f6f7948dbcee3691f4f341ab05d9a8874a67b45ddb4182922eda"}, + {file = "matplotlib-3.11.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:191163532cdefcb1571ca38a6d7e6474baccde64495783e6ba47aa07ec4b9bbb"}, + {file = "matplotlib-3.11.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fdf1c818ab05d0e74002091ddaf414478a3a449ec9d51c8976d45be7e3a01e2"}, + {file = "matplotlib-3.11.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b937b9dba5f5f6c1e31c47abe2186c865c0914fd18f2ce0dfc39c9adcef5951d"}, + {file = "matplotlib-3.11.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f2912f647f3fbe1ccf085f91e213936f9101bead81a5e670565b1f1b3712f4fb"}, + {file = "matplotlib-3.11.1-cp314-cp314t-win_arm64.whl", hash = "sha256:54d47b8ae8b579633a3902ca5b4ad6c1e132a5626d64447b2e22a66394e79987"}, + {file = "matplotlib-3.11.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:427258425f9a3fc4ed79a91f9e9b9aaf5a82cb6571e85dc14063cc6fbb993741"}, + {file = "matplotlib-3.11.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:1ac697e591c11b6ad04679a73c2d2f9980fe9d9f0311fb414a2e329706343dfb"}, + {file = "matplotlib-3.11.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e4b9ac2f1f607ecda2af90a5232beee2af7582fce1cc30c4b6a1b012dc21ee99"}, + {file = "matplotlib-3.11.1.tar.gz", hash = "sha256:69647db5746941c793d6e445a4cd349323ffb87d9cc958c2ad84a659b4832d30"}, ] [package.dependencies] contourpy = ">=1.0.1" cycler = ">=0.10" -fonttools = ">=4.22.0" +fonttools = ">=4.28.2" kiwisolver = ">=1.3.1" numpy = ">=1.25" packaging = ">=20.0" @@ -2578,72 +2578,6 @@ jupyter-server = ">=1.8,<3" [package.extras] test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync"] -[[package]] -name = "numpy" -version = "2.2.6" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.10" -groups = ["main", "dev", "docs"] -markers = "python_version == \"3.12\"" -files = [ - {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf"}, - {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83"}, - {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915"}, - {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680"}, - {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289"}, - {file = "numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d"}, - {file = "numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491"}, - {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a"}, - {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf"}, - {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1"}, - {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab"}, - {file = "numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47"}, - {file = "numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282"}, - {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87"}, - {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249"}, - {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49"}, - {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de"}, - {file = "numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4"}, - {file = "numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566"}, - {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f"}, - {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f"}, - {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868"}, - {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d"}, - {file = "numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd"}, - {file = "numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8"}, - {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f"}, - {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa"}, - {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571"}, - {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1"}, - {file = "numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff"}, - {file = "numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00"}, - {file = "numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd"}, -] - [[package]] name = "numpy" version = "2.3.5" @@ -2651,7 +2585,6 @@ description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.11" groups = ["main", "dev", "docs"] -markers = "python_version >= \"3.13\"" files = [ {file = "numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10"}, {file = "numpy-2.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218"}, @@ -3818,31 +3751,25 @@ files = [ [[package]] name = "skycalc-ipy" -version = "0.7.1" +version = "0.8.0" description = "Get atmospheric spectral information from the ESO skycalc server." optional = false -python-versions = "<3.15,>=3.10" +python-versions = "<3.15,>=3.12" groups = ["main"] files = [ - {file = "skycalc_ipy-0.7.1-py3-none-any.whl", hash = "sha256:499e677ea69bbb2141d94d358c1db97c7d1c48130de2172eda308ba73ceef3ac"}, - {file = "skycalc_ipy-0.7.1.tar.gz", hash = "sha256:687812026a88af0a62bce766afbe2ae840a9fd924a60597b72c50096bcd8f8ed"}, + {file = "skycalc_ipy-0.8.0-py3-none-any.whl", hash = "sha256:f10af694457a806f44160bf7b3ecad485f7ae892ab08c7f52b6b44d3f28be217"}, + {file = "skycalc_ipy-0.8.0.tar.gz", hash = "sha256:fb8bb7bb8dfdf1ef0ec2a198ae23c065a45468579a0f876ebc688eeeaab37d8f"}, ] [package.dependencies] -astar-utils = ">=0.5.3,<1.0" -astropy = [ - {version = ">=6.1.7,<8.0.0", markers = "python_version >= \"3.10\" and python_version < \"3.13\""}, - {version = ">=7.0.2,<8.0.0", markers = "python_version >= \"3.13\""}, -] +astar-utils = ">=0.7.0,<1.0" +astropy = ">=7.0.2,<9.0.0" httpx = ">=0.28.1,<1.0" -numpy = [ - {version = ">=1.26.4,<2.3.0", markers = "python_version >= \"3.10\" and python_version < \"3.13\""}, - {version = ">=2.3.5,<3.0.0", markers = "python_version >= \"3.13\""}, -] +numpy = ">=2.2.6,<3.0.0" pyyaml = ">=6.0.3,<7.0.0" [package.extras] -syn = ["scipy (>=1.15.3,<1.16.0) ; python_version == \"3.10\"", "scipy (>=1.17.0,<1.18.0) ; python_version >= \"3.11\"", "synphot (>=1.7.0,<2.0.0)"] +syn = ["synphot (>=1.7.0,<2.0.0)"] [[package]] name = "sniffio" @@ -4499,4 +4426,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.12,<3.15" -content-hash = "6e74a26a9b71290df95b18c5dc705d907ae78f1e3f6852bbbd935926e5abc637" +content-hash = "720a1af3697025b6b41b99e33093188b5450350c536b7b1f57bed5efe78bc52d" diff --git a/pyproject.toml b/pyproject.toml index 142d75f35..662f8b0bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dependencies = [ "scipy (>=1.17.0,<1.18.0)", "astropy (>=7.2.2,<9.0.0)", - "matplotlib (>=3.10.8,<4.0.0)", + "matplotlib (>=3.11.1,<4.0.0)", "pooch (>=1.9.0,<2.0.0)", "docutils (>=0.22,<1.0)", @@ -33,7 +33,7 @@ dependencies = [ "packaging (>=26.0,<28.0)", "synphot (>=1.7.0,<2.0.0)", - "skycalc-ipy (>=0.7.1,<1.0)", + "skycalc-ipy (>=0.8.0,<1.0)", "anisocado (>=0.6.0,<1.0)", "astar-utils (>=0.7.0,<1.0)" ] From 65181d4c346ea8fce64a3710a44503ac54729f7e Mon Sep 17 00:00:00 2001 From: "Fabian H." <73600109+teutoburg@users.noreply.github.com> Date: Fri, 21 Aug 2026 16:57:07 +0200 Subject: [PATCH 46/68] Delete docs/to-do-list.txt Nobody knew about this, nobody cared, nobody will miss it. --- docs/to-do-list.txt | 107 -------------------------------------------- 1 file changed, 107 deletions(-) delete mode 100644 docs/to-do-list.txt diff --git a/docs/to-do-list.txt b/docs/to-do-list.txt deleted file mode 100644 index 45d661928..000000000 --- a/docs/to-do-list.txt +++ /dev/null @@ -1,107 +0,0 @@ -ScopeSim Development Plan / Wishlist -==================================== - -ScopeSim --------- -- Finish update of spectroscopy mode to incorporate the core code from SpecCADO - WHY: Speeds up SLIT mode, enables expansion to IFU and MOS spectrographs - WHAT: Implement spatial-spectral Field-of-View objects - WHAT: Re-write of FOV-Manager class to accomodate 2+1D FOV objects - -- Simplify Effect object API - WHY: Makes "custom" effect objects easier to write for 3rd parties (e.g. JU Pott's astrometry group) - WHAT: Remove the "fov_grid" method used by FOV-Manager. Replace it with a further apply_to loop when initialising the in-memory instrument model - -- Add 3D Detector-Cube object - WHY: Enables very quick "science grade" spectroscopy data products to be produced - WHY: Paves the way for ETC-mode for ScopeSim - WHAT: Write new DetectorCube class, which piggy-backs the current Detector class - -- Add ETC mode - WHY: Quick generation of ETC table data for delivery to ESO (and whomever else wants it) - WHY: Increases future usability by astronomers when writing observing proposals - WHAT: Add functions for determining SNR through photometry, line integration, or per-pixel - WHAT: Add DetectorETC class, which contains flux and noise images, and returns a table of SNR values - -- Add simple Coronographic Effects (as much as possible) - WHY: Elephant in the room. Needs to be included. - WHAT: An Effect wrapper for a grid of PSFs for different coronographic conditions - -- Add "observatory like" functionality (specifiy RA, Dec, obs date) - WHY: Most often requested feature is to add RA and Dec to observing parameters. - WHY: Atmospheric conditions change with Zenith distance, which is related to RA and Dec - WHY: Helps astronomers better conceptualise their observations - WHAT: ScopeSim will pull in a list of objects from Simbad and add these to the background of the images - WHAT: Link skycalc Effect wrapper to the RA and Dec (i.e. Zenith Distance) parameters - -- Link to external packages like TIPTOP (generic anayltical AO PSF realisations) and PYXEL (generic detector noise simualtions) - WHY: For more accurate realistions of the most important external noise sources - WHAT: Write an interface standard for these two packages. Possibly liase with the developers so that the packages can deliver "ScopeSim-Ready" objects - -- Remove dependency on astropy units - WHY: They are slow - WHAT: Define a set of internal units for Scopesim. Replace all astropy.unit references with internally consistent units propogation - -- Remove dependency on astropy HDU objects - WHY: They are slow. Too many internal checks on the header keywords. Factor of 10x speed increase when using ScopeSim's interal "PoorMansHeader" object - WHAT: Expand PoorMansHeader to cover all use cases, add minimal units checks, replace all occurences of astropy HDU objects - -- Remove dependency on Synphot spectra objects - WHY: They are slow. Based on astropy interpolation objects and astropy units. Too many checks. - WHAT: Write a new high-speed spectral interpolatio class based on the scipy.interpolate objects. - -- Add time-dependency to the observation sequences - WHY: To allow realistic astrometry observations - WHAT: Add a time parameter to the on-sky Source objects - WHAT: Add a OBS_DATE parameter to the OpticalTrain class. This places the objects in the Field-of-View as seen on the date of the observation - - -ScopeSim_Templates ------------------- -- Add ArtPop wrapper. ArtPop is a better (3rd-party) generator for (old and new) stellar population ScopeSim-Source objects. - WHY: Allows users to make more realistic on-sky targets using isochrone libraries. - WHAT: Wrap the ArtPop package into a ScopeSim_Templates sub-module - WHAT: Code and submit a pull request to the ArtPop developers to enable ArtPop to generate ScopeSim-like Source objects - -- Add Vela simualtion wrapper. Vela is a set of high-res simulated galaxies observed over cosmic time - WHY: Allows users to add realisitc galaxies to their images with spatial resolutions better than MICADO for any redshift. Useful for imaging feasibility studies - WHAT: Write a wrapper sub-module that fetches data from the Vela server (or from a copy hosted at Uni-Vienna) - -- Add suite of test objects for pipeline development projects (MICADO, METIS) - WHY: A designated set of pipeline-useful objects helps standardise the objects included (or excluded) from pipeline testing - WHAT: Add the recipes developed by Hugo Buddelmeijer to a MICADO sub-package of ScopeSim_Templates - -- Add stellar population astronometric helper functions - WHY: Makes running astrometric science feasibiltiy cases easier for the users - WHAT: Add the code written by JU-Pott's group for astrometric simulations to the ScopeSim-Templates "stars" sub-package - - -IRDB ----- -- Add a MICADO_ETC package - WHAT: Add consolidated transmission curves, PSFs, detector characteristics to enable high-speed windowed simuations - -- Updates to MICADO, MICADO_Sci, MORFEO, ELT, Armazones packages as new data becomes available - WHAT: Add new modes, updated values and data files - -- Automatic documentation for each of the packages - WHY: Helps authors check whether the package data is contained in the proper format - WHAT: Code to build a documentation page (on RTFD) for each package upon pushing to GitHub - -- Automatic file consistency testing - WHY: Automatic tests let the author know if the file(s) they add will break or significantly alter the expected simulation outputs - WHAT: Code to check units and min, max values - -- Automated instrument performance estimates - WHY: Lets users and authors know what the expected performance of the instrument will be, based on the data files and settings included in the packages - WHAT: Definition of how to determine instrument performance - WHAT: Code for running and extracting the instrument performance parameters - - -Documentation and Testing -------------------------- -- Update and expand astronomy Use-case documentation -- Update and expand Developer use-case documentation -- Update and expand Functional interface (API) documentation -- Write a series of iPython Notebooks as "learning-by-doing" guides -- Maybe a series of youtube videos describing what is happening and how to take advantage of the saved internal information in ScopeSim From 5cfb8c7700485b363e8cd124725b6a8fd11fcfe4 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Fri, 22 Aug 2025 15:10:30 +0200 Subject: [PATCH 47/68] Remove obsolete logic to deal with unversioned versions Those were ancient and unused anyway, so I removed them from the server several months ago, nobody complained since. Without them there, there's no need to keep this functionality around. --- scopesim/server/database.py | 47 +++++++++++++++---------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/scopesim/server/database.py b/scopesim/server/database.py index f7d92560d..e9c80286b 100644 --- a/scopesim/server/database.py +++ b/scopesim/server/database.py @@ -44,29 +44,21 @@ def _get_package_name(package: str) -> str: return package.split(".", maxsplit=1)[0] -def _parse_raw_version(raw_version: str) -> str: - """Catch initial package version which has no date info. - - Set initial package version to basically "minus infinity". - """ - if raw_version in ("", "zip"): - return str(date(1, 1, 1)) - return raw_version.strip(".zip") - - -def _unparse_raw_version(raw_version: str, package_name: str) -> str: - """Turn version string back into full zip folder name. - - If initial version was set with `_parse_raw_version`, revert that. - """ - if raw_version == str(date(1, 1, 1)): - return f"{package_name}.zip" - return f"{package_name}.{raw_version}.zip" - - def _parse_package_version(package: str) -> tuple[str, str]: + """Split zip folder name into package name and version.""" p_name, p_version = package.split(".", maxsplit=1) - return p_name, _parse_raw_version(p_version) + return p_name, p_version.strip(".zip") + + +def _unparse_package_version( + package_name: str, + version: str, + suffix: str | None = None, +) -> str: + """Turn version string back into full zip folder name.""" + if suffix is None: + return f"{package_name}.{version}.zip" + return f"{package_name}.{version}.{suffix}.zip" def _is_stable(package_version: str) -> bool: @@ -123,10 +115,9 @@ def get_all_latest(version_groups: _GrpVerType) -> Iterator[tuple[str, str]]: def group_package_versions(all_packages: Iterable[tuple[str, str]]) -> _GrpItrType: """Group different versions of packages by package name.""" - version_groups = groupby_transform(sorted(all_packages), - keyfunc=first, - valuefunc=last, - reducefunc=list) + version_groups = groupby_transform( + sorted(all_packages), keyfunc=first, valuefunc=last, reducefunc=list, + ) return version_groups @@ -269,7 +260,7 @@ def list_packages(pkg_name: str | None = None) -> list[str]: if pkg_name not in all_grouped: raise ValueError(f"Package name {pkg_name} not found on server.") - p_versions = [_unparse_raw_version(version, pkg_name) + p_versions = [_unparse_package_version(pkg_name, version) for version in all_grouped[pkg_name]] return p_versions @@ -280,14 +271,14 @@ def _get_zipname(pkg_name: str, release: str, all_versions) -> str: elif release == "latest": zip_name = get_latest(all_versions[pkg_name]) else: - release = _parse_raw_version(release) + release = release.strip(".zip") if release not in all_versions[pkg_name]: msg = (f"Requested version '{release}' of '{pkg_name}' package" " could not be found on the server. Available versions " f"are: {all_versions[pkg_name]}") raise ValueError(msg) zip_name = release - return _unparse_raw_version(zip_name, pkg_name) + return _unparse_package_version(pkg_name, zip_name) def _download_single_package( From 27fa0087f62c907a3c2a61b8702c87b93bbafcd9 Mon Sep 17 00:00:00 2001 From: Kieran Leschinski Date: Wed, 19 Aug 2026 15:30:07 +0200 Subject: [PATCH 48/68] Fix NameError in SpectralTrace.plot when plot_trace_id is used without plot_outline The corner points of the trace outline were only computed inside the plot_outline branch, but the plot_trace_id branch uses them to position the label. Compute them whenever either option needs them. Adds a parametrised regression test. --- scopesim/effects/spectral_trace_list_utils.py | 4 +++- .../tests_effects/test_SpectralTraceListUtils.py | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/scopesim/effects/spectral_trace_list_utils.py b/scopesim/effects/spectral_trace_list_utils.py index c67c1d1ad..bd80b4490 100644 --- a/scopesim/effects/spectral_trace_list_utils.py +++ b/scopesim/effects/spectral_trace_list_utils.py @@ -626,7 +626,7 @@ def plot(self, wave_min=None, wave_max=None, xi_min=None, xi_max=None, *, if plot_ctrlpnts: axes.plot(x, y, "o", c=c) - if plot_outline: + if plot_outline or plot_trace_id: blue_end = self.table[mask][w == w.min()] red_end = self.table[mask][w == w.max()] blue_end.sort(sname) @@ -634,6 +634,8 @@ def plot(self, wave_min=None, wave_max=None, xi_min=None, xi_max=None, *, corners = vstack([blue_end[[0, -1]][xname, yname], red_end[[-1, 0]][xname, yname], blue_end[0][xname, yname]]) + + if plot_outline: axes.plot(corners[xname], corners[yname], c=c) if plot_trace_id: diff --git a/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py b/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py index 21dadc9ae..3ce177aaa 100644 --- a/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py +++ b/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py @@ -36,6 +36,17 @@ def test_determines_correct_dispersion_axis_y(self): spt = SpectralTrace(trace_tbl) assert spt.dispersion_axis == 'y' +class TestSpectralTracePlot: + """Tests for SpectralTrace.plot()""" + @pytest.mark.parametrize("plot_outline", [True, False]) + def test_plot_trace_id_works_without_outline(self, plot_outline): + # plot_trace_id previously relied on corners, which was only + # computed when plot_outline was True + spt = SpectralTrace(tlo.trace_1(), trace_id="TRACE_1") + axes = spt.plot(plot_trace_id=True, plot_outline=plot_outline) + assert any(txt.get_text() == spt.trace_id for txt in axes.texts) + + class TestPowerVec: """Test function power_vector()""" def test_gives_correct_result(self): From 9598905c95d0ffb9165bd049b74c6edd8f95e2e5 Mon Sep 17 00:00:00 2001 From: Kieran Leschinski Date: Wed, 19 Aug 2026 15:33:16 +0200 Subject: [PATCH 49/68] Fix XiLamImage overwriting the cunit of its primary WCS The last line of the wcsa setup block assigned to self.wcs.wcs.cunit instead of self.wcsa.wcs.cunit, discarding the arcsec unit of the slit axis on the primary WCS of the rectified spectrum. Adds a unit test with a small synthetic cube covering both WCSs. --- scopesim/effects/spectral_trace_list_utils.py | 2 +- .../test_SpectralTraceListUtils.py | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/scopesim/effects/spectral_trace_list_utils.py b/scopesim/effects/spectral_trace_list_utils.py index bd80b4490..15351a178 100644 --- a/scopesim/effects/spectral_trace_list_utils.py +++ b/scopesim/effects/spectral_trace_list_utils.py @@ -767,7 +767,7 @@ def __init__(self, fov, dlam_per_pix): self.wcsa.wcs.cdelt = [d_lam, 1./n_xi] self.wcsa.wcs.ctype = ["LINEAR", "LINEAR"] self.wcsa.wcs.cname = ["WAVELEN", "SLITPOS"] - self.wcs.wcs.cunit = ["um", ""] + self.wcsa.wcs.cunit = ["um", ""] self.xi = self.wcs.all_pix2world(self.lam[0], np.arange(n_xi), 0)[1] self.npix_xi = n_xi diff --git a/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py b/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py index 3ce177aaa..215f869ed 100644 --- a/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py +++ b/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py @@ -9,9 +9,12 @@ from astropy.io import fits +from astropy import units as u + from scopesim.effects.spectral_trace_list_utils import SpectralTrace from scopesim.effects.spectral_trace_list_utils import Transform2D, power_vector from scopesim.effects.spectral_trace_list_utils import make_image_interpolations +from scopesim.effects.spectral_trace_list_utils import XiLamImage from scopesim.tests.mocks.py_objects import trace_list_objects as tlo class TestSpectralTrace: @@ -145,6 +148,30 @@ def test_grid_false_shape_is_preserved(self, tf2d): assert res.shape == (n_y, n_x) +class MockCubeFov: + """Minimal stand-in for a FieldOfView carrying a spectral cube.""" + def __init__(self, n_lam=20, n_eta=3, n_xi=11): + hdr = fits.Header() + hdr["NAXIS"] = 3 + hdr["NAXIS1"], hdr["NAXIS2"], hdr["NAXIS3"] = n_xi, n_eta, n_lam + hdr["CTYPE1"], hdr["CTYPE2"], hdr["CTYPE3"] = "LINEAR", "LINEAR", "WAVE" + hdr["CRPIX1"], hdr["CRPIX2"], hdr["CRPIX3"] = 1, 1, 1 + hdr["CRVAL1"], hdr["CRVAL2"], hdr["CRVAL3"] = 0., 0., 2.0 + hdr["CDELT1"], hdr["CDELT2"], hdr["CDELT3"] = 0.1, 0.1, 0.001 + hdr["CUNIT1"], hdr["CUNIT2"], hdr["CUNIT3"] = "arcsec", "arcsec", "um" + self.cube = fits.ImageHDU( + data=np.ones((n_lam, n_eta, n_xi)), header=hdr) + self.meta = {"xi_min": -0.5 * u.arcsec, "xi_max": 0.5 * u.arcsec} + + +class TestXiLamImage: + def test_primary_wcs_keeps_arcsec_cunit(self): + # The wcsa block previously overwrote self.wcs.wcs.cunit + xilam = XiLamImage(MockCubeFov(), dlam_per_pix=0.001) + assert list(xilam.wcs.wcs.cunit) == [u.um, u.arcsec] + assert list(xilam.wcsa.wcs.cunit) == [u.um, u.dimensionless_unscaled] + + class TestImageInterpolations: """Tests for function make_image_interpolations""" def test_return_empty_for_table(self): From 0ff722019187ba0fd60b770538a581fb303b43ad Mon Sep 17 00:00:00 2001 From: Kieran Leschinski Date: Wed, 19 Aug 2026 15:33:53 +0200 Subject: [PATCH 50/68] Make Transform2D per-call transform overrides non-persistent pretransform_x/pretransform_y/posttransform keyword arguments passed to Transform2D.__call__ were assigned to the instance, permanently rewiring the transform for all subsequent calls. Resolve the overrides into local variables instead; the documented per-call override behaviour is kept. Adds a regression test asserting the instance is unchanged after an overridden call. --- scopesim/effects/spectral_trace_list_utils.py | 25 +++++++++++-------- .../test_SpectralTraceListUtils.py | 11 ++++++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/scopesim/effects/spectral_trace_list_utils.py b/scopesim/effects/spectral_trace_list_utils.py index bd80b4490..a23bf16f3 100644 --- a/scopesim/effects/spectral_trace_list_utils.py +++ b/scopesim/effects/spectral_trace_list_utils.py @@ -848,7 +848,8 @@ def __call__(self, x, y, grid=False, **kwargs): this case, x and y must be of the same length). Functions `pretransform_x`, `pretransform_y` and `posttransform` - can be supplied to override the instance values. + can be supplied to override the instance values for this call only; + the instance itself is not modified. Parameters ---------- @@ -863,12 +864,16 @@ def __call__(self, x, y, grid=False, **kwargs): in x and y. When grid=False, a vector. In this case, x and y must have the same length. """ + # Per-call overrides must not modify the instance + pretransform_x = self.pretransform_x + pretransform_y = self.pretransform_y + posttransform = self.posttransform if "pretransform_x" in kwargs: - self.pretransform_x = self._repackage(kwargs["pretransform_x"]) + pretransform_x = self._repackage(kwargs["pretransform_x"]) if "pretransform_y" in kwargs: - self.pretransform_y = self._repackage(kwargs["pretransform_y"]) + pretransform_y = self._repackage(kwargs["pretransform_y"]) if "posttransform" in kwargs: - self.posttransform = self._repackage(kwargs["posttransform"]) + posttransform = self._repackage(kwargs["posttransform"]) x = np.array(x) y = np.array(y) @@ -879,10 +884,10 @@ def __call__(self, x, y, grid=False, **kwargs): "is False") # Apply pre transforms - if self.pretransform_x is not None: - x = self.pretransform_x[0](x, **self.pretransform_x[1]) - if self.pretransform_y is not None: - y = self.pretransform_y[0](y, **self.pretransform_y[1]) + if pretransform_x is not None: + x = pretransform_x[0](x, **pretransform_x[1]) + if pretransform_y is not None: + y = pretransform_y[0](y, **pretransform_y[1]) xvec = power_vector(x.flatten(), self.nx - 1) yvec = power_vector(y.flatten(), self.ny - 1) @@ -902,8 +907,8 @@ def __call__(self, x, y, grid=False, **kwargs): result = result.reshape(orig_shape) # Apply posttransform - if self.posttransform is not None: - result = self.posttransform[0](result, **self.posttransform[1]) + if posttransform is not None: + result = posttransform[0](result, **posttransform[1]) return result diff --git a/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py b/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py index 3ce177aaa..9c970d293 100644 --- a/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py +++ b/scopesim/tests/tests_effects/test_SpectralTraceListUtils.py @@ -139,6 +139,17 @@ def test_fit_gives_correct_matrix(self): assert tf2d.matrix == pytest.approx(matrix) + def test_call_kwargs_do_not_modify_instance(self, quadratic): + # pre/posttransform overrides passed to __call__ previously + # rewired the instance for all subsequent calls + tf2d = Transform2D(quadratic['matrix']) + x, y = 0.7, -0.3 + plain = tf2d(x, y) + shifted = tf2d(x, y, posttransform=lambda z: z + 100) + assert shifted == pytest.approx(plain + 100) + assert tf2d.posttransform is None + assert tf2d(x, y) == pytest.approx(plain) + def test_grid_false_shape_is_preserved(self, tf2d): n_x, n_y = 4, 2 res = tf2d(np.ones((n_y, n_x)), np.ones((n_y, n_x)), grid=False) From cf8b231611f65e761ecbed1ffbf6f477c410ddef Mon Sep 17 00:00:00 2001 From: "astarvienna-bot[bot]" <217837468+myciapp[bot]@users.noreply.github.com> Date: Mon, 24 Aug 2026 13:03:43 +0000 Subject: [PATCH 51/68] Bumping version from 0.12.0b2.dev1 to 0.12.0b2.dev2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 662f8b0bf..942aa42f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ScopeSim" -version = "0.12.0b2.dev1" +version = "0.12.0b2.dev2" description = "Generalised telescope observation simulator" license = {text = "GPL-3.0-or-later"} authors = [ From 74b410834d460e710cf0a35990ec03d1da00d079 Mon Sep 17 00:00:00 2001 From: Kieran Leschinski Date: Wed, 19 Aug 2026 15:52:33 +0200 Subject: [PATCH 52/68] Cache the detector layout file read in MetisLMS fov_grid MetisLMSSpectralTrace.fov_grid re-read and re-parsed the detector layout file from disk on every call. During construction of an LMS trace list it is called several times for each of the 28 slices (once per trace at construction, again through update_meta), so the same small file was read dozens of times. Wrap the read in a small lru_cache'd module function. Adds a test that the file is read only once for repeated calls. --- scopesim/effects/metis_lms_trace_list.py | 14 ++++++++++- .../tests_effects/test_MetisLMSTraceList.py | 24 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/scopesim/effects/metis_lms_trace_list.py b/scopesim/effects/metis_lms_trace_list.py index 76ba9b298..aefaf3ea6 100644 --- a/scopesim/effects/metis_lms_trace_list.py +++ b/scopesim/effects/metis_lms_trace_list.py @@ -3,6 +3,7 @@ import copy import warnings +from functools import lru_cache from tqdm.auto import tqdm import numpy as np @@ -28,6 +29,17 @@ logger = get_logger(__name__) +@lru_cache(maxsize=8) +def _read_detector_layout(filename): + """Read (and cache) a detector layout file. + + The layout is required by ``MetisLMSSpectralTrace.fov_grid``, which is + called several times for each of the 28 slices; without the cache the + same file is read and parsed from disk on every call. + """ + return ioascii.read(find_file(filename)) + + class MetisLMSSpectralTraceList(SpectralTraceList): """SpectralTraceList for the METIS LM spectrograph.""" @@ -323,7 +335,7 @@ def fov_grid(self): y_max = aperture["top"] filename_det_layout = from_currsys("!DET.layout.file_name", cmds=self.cmds) - layout = ioascii.read(find_file(filename_det_layout)) + layout = _read_detector_layout(filename_det_layout) det_lims = {} xhw = layout["pixel_size"] * layout["x_size"] / 2 yhw = layout["pixel_size"] * layout["y_size"] / 2 diff --git a/scopesim/tests/tests_effects/test_MetisLMSTraceList.py b/scopesim/tests/tests_effects/test_MetisLMSTraceList.py index 7fbfd277b..3cf1dc096 100644 --- a/scopesim/tests/tests_effects/test_MetisLMSTraceList.py +++ b/scopesim/tests/tests_effects/test_MetisLMSTraceList.py @@ -16,6 +16,30 @@ def patch_mock_path_metis(mock_dir): with patch("scopesim.rc.__search_path__", [metis_dir]): yield +class TestDetectorLayoutCache: + def test_layout_file_is_read_only_once(self, mock_dir, monkeypatch): + from scopesim.effects import metis_lms_trace_list as mlt + + mlt._read_detector_layout.cache_clear() + calls = [] + real_read = mlt.ioascii.read + + def counting_read(*args, **kwargs): + calls.append(args) + return real_read(*args, **kwargs) + + monkeypatch.setattr(mlt.ioascii, "read", counting_read) + + path = str(mock_dir / "files" / "LIST_detector_layout.dat") + first = mlt._read_detector_layout(path) + second = mlt._read_detector_layout(path) + + assert len(calls) == 1 + assert first is second + mlt._read_detector_layout.cache_clear() + + + @pytest.mark.usefixtures("patch_mock_path_metis") class TestPredisperserAngle: @pytest.mark.parametrize("coeffs,wavelen,expected", From 2b4f726a21d61f6fb549b1844e95b474eb77bdb3 Mon Sep 17 00:00:00 2001 From: oczoske Date: Mon, 24 Aug 2026 16:29:42 +0200 Subject: [PATCH 53/68] Define N-band lasers --- scopesim/effects/metis_wcu/metis_wcu.py | 26 ++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/scopesim/effects/metis_wcu/metis_wcu.py b/scopesim/effects/metis_wcu/metis_wcu.py index 7f8659bc8..810bb97d3 100644 --- a/scopesim/effects/metis_wcu/metis_wcu.py +++ b/scopesim/effects/metis_wcu/metis_wcu.py @@ -477,29 +477,45 @@ def _laser_intensity(self): # Laser 2 (tunable), power divided among multiple lines if not isinstance(self.meta['laser_t_wave'], Iterable): self.meta['laser_t_wave'] = [self.meta['laser_t_wave']] - lam_t = self.meta["laser_t_wave"] * u.um - nline = len(lam_t) - power_t = self.meta["laser_t_power"] * u.W / (c.c * c.h / lam) * u.ph / nline + lamc_t = self.meta["laser_t_wave"] * u.um + nline_t = len(lamc_t) + power_t = self.meta["laser_t_power"] * u.W / (c.c * c.h / lam) * u.ph / nline_t # Laser 3 (M band) lamc_m = self.meta["laser_m_wave"] * u.um power_m = self.meta["laser_m_power"] * u.W / (c.c * c.h / lamc_m) * u.ph + # N-band lasers + if not isinstance(self.meta['laser_n_wave'], Iterable): + self.meta['laser_n_wave'] = [self.meta['laser_n_wave']] + if not isinstance(self.meta['laser_n_power'], Iterable): + self.meta['laser_n_power'] = [self.meta['laser_n_power']] + print(self.meta['laser_n_wave']) + lamc_n = self.meta["laser_n_wave"] * u.um + print(self.meta['laser_n_power']) + power_n = self.meta["laser_n_power"] * u.W / (c.c * c.h / lamc_n) * u.ph + + # Apply fibre transmission power_l *= self.fibre_trans power_t *= self.fibre_trans power_m *= self.fibre_trans + power_n *= self.fibre_trans sigma = 2 * dlam + print(sigma) amp = 1/(sigma * np.sqrt(2 * np.pi)) line_l = Gaussian1D(amplitude=amp, mean=lamc_l, stddev=sigma) line_m = Gaussian1D(amplitude=amp, mean=lamc_m, stddev=sigma) - list_t = [Gaussian1D(amplitude=amp, mean=ll, stddev=sigma) for ll in lam_t] + list_t = [Gaussian1D(amplitude=amp, mean=ll, stddev=sigma) for ll in lamc_t] line_t = sum(list_t[1:], start=list_t[0]) + list_n = [Gaussian1D(amplitude=pp*amp, mean=ll, stddev=sigma) for (ll,pp) in + zip(lamc_n, power_n)] + line_n = sum(list_n[1:], start=list_n[0]) flux = ((power_l * line_l(lam) + power_m * line_m(lam) + power_t - * line_t(lam)) / (np.pi * self.d_is**2)) + * line_t(lam) + line_n(lam)) / (np.pi * self.d_is**2)) # emergent intensity from the IS output port intens = mult_is * flux / (np.pi * u.sr) From 1f30931facfb0b6d63d7b7d7b1ce95610f694d06 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Mon, 24 Aug 2026 17:54:05 +0200 Subject: [PATCH 54/68] More useful error when download fails --- scopesim/server/database.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scopesim/server/database.py b/scopesim/server/database.py index b1246b9ee..0800b50b1 100644 --- a/scopesim/server/database.py +++ b/scopesim/server/database.py @@ -410,7 +410,11 @@ def download_packages( def download_missing_pkgs(instrument: str) -> None: """Download instrument package and required support packages.""" # First download package itself - zip_file = download_packages([instrument])[0] + downloaded = download_packages([instrument]) + if not downloaded: + raise RuntimeError(f"Could not download \"{instrument}\".") + + zip_file = downloaded[0] # If package needs other packages, download them as well defyam = zip_file.with_suffix("") / "default.yaml" From 32469ad41c6beb0d8a3c6f8daa9c55bf7efeccf4 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Mon, 25 Mar 2024 16:51:56 +0100 Subject: [PATCH 55/68] Improve handling of spectral trace mapping and rectification - raise `ValueError` when `xlim_mm` is None (`footprint` could be improved) - remove `kwargs` from `.rectify()`, make all args explicit - raise `KeyError` when `xi_min` or `xi_max` are neither in args nor header - some typing and formatting along the way --- scopesim/effects/metis_lms_trace_list.py | 88 +++++++---- scopesim/effects/spectral_trace_list.py | 41 +++-- scopesim/effects/spectral_trace_list_utils.py | 141 +++++++++++------- 3 files changed, 180 insertions(+), 90 deletions(-) diff --git a/scopesim/effects/metis_lms_trace_list.py b/scopesim/effects/metis_lms_trace_list.py index aefaf3ea6..877360da0 100644 --- a/scopesim/effects/metis_lms_trace_list.py +++ b/scopesim/effects/metis_lms_trace_list.py @@ -17,9 +17,11 @@ from ..utils import from_currsys, find_file, quantify, get_logger from .spectral_trace_list import SpectralTraceList -from .spectral_trace_list_utils import SpectralTrace -from .spectral_trace_list_utils import Transform2D -from .spectral_trace_list_utils import make_image_interpolations +from .spectral_trace_list_utils import ( + SpectralTrace, + Transform2D, + make_image_interpolations, +) from .apertures import ApertureMask from .ter_curves import TERCurve from ..optics.fov import FieldOfView, FieldOfView3D @@ -126,28 +128,44 @@ def apply_to(self, obj, **kwargs): slicecube = np.zeros((n_z, ny_slice, n_x)) for islice in range(n_z): - ifov = RectBivariateSpline(np.arange(n_y), - np.arange(n_x), - fovcube[islice], kx=1, ky=1) + ifov = RectBivariateSpline( + np.arange(n_y), + np.arange(n_x), + fovcube[islice], + kx=1, + ky=1, + ) slicecube[islice] = ifov(yfov, xfov, grid=False) - slicefov = FieldOfView3D(obj.header, - [obj.meta["wave_min"], - obj.meta["wave_max"]]) + slicefov = FieldOfView3D( + obj.header, + [obj.meta["wave_min"], obj.meta["wave_max"]], + ) slicefov.detector_header = obj.detector_header slicefov.meta["xi_min"] = obj.meta["xi_min"] slicefov.meta["xi_max"] = obj.meta["xi_max"] slicefov.meta["trace_id"] = sptid - slicefov.cube = fits.ImageHDU(header=slicewcs.to_header(), - data=slicecube) + slicefov.cube = fits.ImageHDU( + header=slicewcs.to_header(), + data=slicecube, + ) # slicefov.cube.writeto(f"slicefov_{sptid}.fits", overwrite=True) - slicefov.hdu = spt.map_spectra_to_focal_plane(slicefov) - if slicefov.hdu is not None: - sxmin = slicefov.hdu.header["XMIN"] - sxmax = slicefov.hdu.header["XMAX"] - symin = slicefov.hdu.header["YMIN"] - symax = slicefov.hdu.header["YMAX"] - fovimage[symin:symax, sxmin:sxmax] += slicefov.hdu.data + + try: + # If footprint is outside FOV, this will assign None to the hdu + slicefov.hdu = spt.map_spectra_to_focal_plane(slicefov) + except ValueError as err: # xlim_mm is None + logger.error(err) + continue + + if slicefov.hdu is None: # footprint outside FOV + continue + + sxmin = slicefov.hdu.header["XMIN"] + sxmax = slicefov.hdu.header["XMAX"] + symin = slicefov.hdu.header["YMIN"] + symax = slicefov.hdu.header["YMAX"] + fovimage[symin:symax, sxmin:sxmax] += slicefov.hdu.data obj.hdu = fits.ImageHDU(data=fovimage, header=obj.detector_header) @@ -163,11 +181,13 @@ def make_spectral_traces(self): self.meta["predisperser"] = tempres["Predisperser"] spec_traces = {} - for sli in np.arange(self.meta["nslice"]): - slicename = "Slice " + str(sli + 1) + for sli in range(self.meta["nslice"]): + slicename = f"Slice {sli + 1}" spec_traces[slicename] = MetisLMSSpectralTrace( self._file, - spslice=sli, params=self.meta) + spslice=sli, + params=self.meta, + ) self.spectral_traces = spec_traces @@ -235,11 +255,25 @@ def rectify_cube(self, hdulist, xi_min=None, xi_max=None, interps=None, for i, spt in enumerate(self.spectral_traces.values()): spt.wave_min = wave_min spt.wave_max = wave_max - result = spt.rectify(hdulist, interps=interps, - wave_min=wave_min, wave_max=wave_max, - xi_min=xi_min, xi_max=xi_max, - bin_width=dwave, - fit_inverse=fit_inverse) + + try: + result = spt.rectify( + hdulist, + interps=interps, + wave_min=wave_min, + wave_max=wave_max, + xi_min=xi_min, + xi_max=xi_max, + bin_width=dwave, + fit_inverse=fit_inverse, + ) + except (KeyError, ValueError) as err: + logger.error(err) + continue + + if result is None: # Outside filter range + continue + cube[:, i, :] = result.data.T # FIXME: use wcs object here @@ -307,7 +341,7 @@ def __init__(self, hdulist, spslice, params, **kwargs): super().__init__(polyhdu, **params) self._file = hdulist - self.meta["description"] = "Slice " + str(spslice + 1) + self.meta["description"] = f"Slice {spslice + 1}" self.meta["trace_id"] = f"Slice {spslice + 1}" self.meta.update(params) # Provisional: diff --git a/scopesim/effects/spectral_trace_list.py b/scopesim/effects/spectral_trace_list.py index 2b4e8980e..f0583baf9 100644 --- a/scopesim/effects/spectral_trace_list.py +++ b/scopesim/effects/spectral_trace_list.py @@ -14,13 +14,13 @@ from astropy.io import fits from astropy.table import Table -from .effects import Effect -from .ter_curves import FilterCurve -from .spectral_trace_list_utils import SpectralTrace, make_image_interpolations +from ..utils import from_currsys, check_keys, figure_factory, get_logger from ..optics.image_plane_utils import header_from_list_of_xy from ..optics.fov import FieldOfView from ..optics.fov_volume_list import FovVolumeList -from ..utils import from_currsys, check_keys, figure_factory, get_logger +from .effects import Effect +from .ter_curves import FilterCurve +from .spectral_trace_list_utils import SpectralTrace, make_image_interpolations logger = get_logger(__name__) @@ -257,7 +257,11 @@ def apply_to(self, obj, **kwargs): self.update_meta() spt = self.spectral_traces[obj.trace_id] - obj.hdu = spt.map_spectra_to_focal_plane(obj) + try: + # If footprint is outside FOV, this will assign None to the hdu + obj.hdu = spt.map_spectra_to_focal_plane(obj) + except ValueError as err: # xlim_mm is None + logger.error(err) logger.debug("%s done", self.display_name) return obj @@ -358,14 +362,25 @@ def rectify_traces(self, hdulist, xi_min=None, xi_max=None, interps=None, for i, trace_id in tqdm(enumerate(self.spectral_traces, start=1), desc=" Traces", total=len(self.spectral_traces)): - hdu = self[trace_id].rectify(hdulist, - interps=interps, - bin_width=bin_width, - xi_min=xi_min, xi_max=xi_max, - wave_min=wave_min, wave_max=wave_max) - if hdu is not None: # ..todo: rectify does not do that yet - outhdul.append(hdu) - outhdul[0].header[f"EXTNAME{i}"] = trace_id + try: + hdu = self[trace_id].rectify( + hdulist, + interps=interps, + bin_width=bin_width, + xi_min=xi_min, + xi_max=xi_max, + wave_min=wave_min, + wave_max=wave_max, + ) + except (KeyError, ValueError) as err: + logger.error(err) + continue + + if hdu is None: # TODO: rectify does not do that yet + continue + + outhdul.append(hdu) + outhdul[0].header[f"EXTNAME{i}"] = trace_id outhdul[0].header.update(inhdul[0].header) diff --git a/scopesim/effects/spectral_trace_list_utils.py b/scopesim/effects/spectral_trace_list_utils.py index d523aee72..721753371 100644 --- a/scopesim/effects/spectral_trace_list_utils.py +++ b/scopesim/effects/spectral_trace_list_utils.py @@ -24,8 +24,14 @@ from astropy.wcs import WCS from astropy.modeling.models import Polynomial2D -from ..utils import (power_vector, quantify, from_currsys, close_loop, - figure_factory, get_logger) +from ..utils import ( + power_vector, + quantify, + from_currsys, + close_loop, + figure_factory, + get_logger, +) logger = get_logger(__name__) @@ -154,7 +160,8 @@ def compute_interpolation_functions(self): logger.info( "Dispersion axis determined to be %s", self.dispersion_axis) - def map_spectra_to_focal_plane(self, fov): + # TODO: add typing to fov: FieldOfView, but that creates a circular import + def map_spectra_to_focal_plane(self, fov) -> fits.ImageHDU: """ Apply the spectral trace mapping to a spectral cube. @@ -171,12 +178,15 @@ def map_spectra_to_focal_plane(self, fov): wave_max = fov.meta["wave_max"].value # [um] xi_min = fov.meta["xi_min"].value # [arcsec] xi_max = fov.meta["xi_max"].value # [arcsec] - xlim_mm, ylim_mm = self.footprint(wave_min=wave_min, wave_max=wave_max, - xi_min=xi_min, xi_max=xi_max) + xlim_mm, ylim_mm = self.footprint( + wave_min=wave_min, + wave_max=wave_max, + xi_min=xi_min, + xi_max=xi_max, + ) if xlim_mm is None: - logger.warning("xlim_mm is None") - return None + raise ValueError("xlim_mm is None") fov_header = fov.header det_header = fov.detector_header @@ -235,12 +245,10 @@ def map_spectra_to_focal_plane(self, fov): xilam_wcs = xilam.wcs # focal-plane coordinate images - ximg_fpa, yimg_fpa = np.meshgrid(np.linspace(xmin_mm, xmax_mm, - sub_naxis1, - dtype=np.float32), - np.linspace(ymin_mm, ymax_mm, - sub_naxis2, - dtype=np.float32)) + ximg_fpa, yimg_fpa = np.meshgrid( + np.linspace(xmin_mm, xmax_mm, sub_naxis1, dtype=np.float32), + np.linspace(ymin_mm, ymax_mm, sub_naxis2, dtype=np.float32), + ) # Image mapping (xi, lambda) on the focal plane xi_fpa = self.xy2xi(ximg_fpa, yimg_fpa).astype(np.float32) @@ -276,8 +284,9 @@ def map_spectra_to_focal_plane(self, fov): # Scale to ph / s / pixel dlam_by_dx, dlam_by_dy = self.xy2lam.gradient() - dlam_per_pix = pixsize * np.sqrt(dlam_by_dx(ximg_fpa, yimg_fpa)**2 + - dlam_by_dy(ximg_fpa, yimg_fpa)**2) + dlam_per_pix = pixsize * np.sqrt( + dlam_by_dx(ximg_fpa, yimg_fpa)**2 + + dlam_by_dy(ximg_fpa, yimg_fpa)**2) image *= pixscale * dlam_per_pix # [arcsec/pix] * [um/pix] # img_header = sub_wcs.to_header() @@ -289,15 +298,35 @@ def map_spectra_to_focal_plane(self, fov): img_header["YMAX"] = ymax if np.any(image < 0): - logger.warning("map_spectra_to_focal_plane: %d negative pixels", - np.sum(image < 0)) + logger.warning( + "map_spectra_to_focal_plane: %d negative pixels", + np.sum(image < 0)) image_hdu = fits.ImageHDU(header=img_header, data=image) return image_hdu - def rectify(self, hdulist, interps=None, wcs=None, **kwargs): + def rectify( + self, + hdulist: fits.HDUList, + interps: list | None = None, + wcs: WCS | None = None, + fit_inverse: bool = True, + bin_width: float | None = None, + wave_min: float | None = None, + wave_max: float | None = None, + xi_min: float | None = None, + xi_max: float | None = None, + ) -> fits.ImageHDU | None: """Create 2D spectrum for a trace. + .. todo: Consider turning `interps` into a dict keyed on extname. + + .. todo: Proper typing on `interps`. + + .. todo: Rewrite and expand docstring (FH). + + .. todo: Consider using quantities for float parameters. + Parameters ---------- hdulist : HDUList @@ -323,22 +352,21 @@ def rectify(self, hdulist, interps=None, wcs=None, **kwargs): Spatial limits of the slit on the sky. This should be taken from the header of the hdulist, but this is not yet provided by scopesim """ - self.fit_inverse = kwargs.get("fit_inverse", True) - wave_min = kwargs.get("wave_min", - self.wave_min) - wave_max = kwargs.get("wave_max", - self.wave_max) + # Note: fit_inverse used to be self.fit_inverse, but wasn't used + # outside this function. + wave_min = wave_min if wave_min is not None else self.wave_min + wave_max = wave_max if wave_max is not None else self.wave_max if wave_max < self.wave_min or wave_min > self.wave_max: logger.debug(" Outside filter range") return None + wave_min = max(wave_min, self.wave_min) wave_max = min(wave_max, self.wave_max) logger.info("Rectifying %s (%.02f .. %.02f um)", self.trace_id, wave_min, wave_max) # bin_width is taken as the minimum dispersion of the trace - # ..todo: if wcs is given take bin width from cdelt1 - bin_width = kwargs.get("bin_width", None) + # TODO: if wcs is given take bin width from cdelt1 if bin_width is None: self._set_dispersion(wave_min, wave_max) bin_width = np.abs(self.dlam_per_pix.y).min() @@ -347,20 +375,23 @@ def rectify(self, hdulist, interps=None, wcs=None, **kwargs): pixscale = from_currsys(self.meta["pixel_scale"], self.cmds) # Temporary solution to get slit length - xi_min = kwargs.get("xi_min", None) if xi_min is None: try: xi_min = hdulist[0].header["HIERARCH INS SLIT XIMIN"] - except KeyError: - logger.error("xi_min not found") - return None - xi_max = kwargs.get("xi_max", None) + except KeyError as err: + raise KeyError( + "xi_min must be in header as 'INS SLIT XIMIN' or provided " + "directly as an argument." + ) from err + if xi_max is None: try: xi_max = hdulist[0].header["HIERARCH INS SLIT XIMAX"] - except KeyError: - logger.error("xi_max not found") - return None + except KeyError as err: + raise KeyError( + "xi_max must be in header as 'INS SLIT XIMAX' or provided " + "directly as an argument." + ) from err if wcs is None: wcs = WCS(naxis=2) @@ -381,20 +412,26 @@ def rectify(self, hdulist, interps=None, wcs=None, **kwargs): interps = make_image_interpolations(hdulist, kx=1, ky=1) # Create Xi, Lam images (do I need Iarr and Jarr or can I build Xi, Lam directly?) - Iarr, Jarr = np.meshgrid(np.arange(nlam, dtype=np.float32), - np.arange(nxi, dtype=np.float32)) + Iarr, Jarr = np.meshgrid( + np.arange(nlam, dtype=np.float32), + np.arange(nxi, dtype=np.float32), + ) Lam, Xi = wcs.all_pix2world(Iarr, Jarr, 0) # Make sure that we do have microns Lam = Lam * u.Unit(wcs.wcs.cunit[0]).to(u.um) - ## TODO: Replace self.xilam2x and self.xilam2y by fitted - ## functions fit_xilam2x and fit_xilam2y, which need - ## to be determined by mapping a grid of (xi, lam) to (x,y) - ## and back to (xip, lamp); then fit (x,y)(xip, lamp). - if self.fit_inverse: - fit_xilam2x, fit_xilam2y = self._fit_transform(xi_min, xi_max, - wave_min, wave_max) + # TODO: Replace self.xilam2x and self.xilam2y by fitted + # functions fit_xilam2x and fit_xilam2y, which need + # to be determined by mapping a grid of (xi, lam) to (x,y) + # and back to (xip, lamp); then fit (x,y)(xip, lamp). + if fit_inverse: + fit_xilam2x, fit_xilam2y = self._fit_transform( + xi_min, + xi_max, + wave_min, + wave_max, + ) else: logger.debug("Using matrix transform") fit_xilam2x, fit_xilam2y = self.xilam2x, self.xilam2y @@ -527,14 +564,18 @@ def footprint(self, wave_min=None, wave_max=None, xi_min=None, xi_max=None): # Map the edges of xi/lam to the focal plance n_edge = 512 - wave_edge = np.concatenate((np.linspace(wave_min, wave_max, n_edge), - [wave_max] * n_edge, - np.linspace(wave_min, wave_max, n_edge), - [wave_min] * n_edge)) - xi_edge = np.concatenate(([xi_min] * n_edge, - np.linspace(xi_min, xi_max, n_edge), - [xi_max] * n_edge, - np.linspace(xi_min, xi_max, n_edge))) + wave_edge = np.concatenate(( + np.linspace(wave_min, wave_max, n_edge), + [wave_max] * n_edge, + np.linspace(wave_min, wave_max, n_edge), + [wave_min] * n_edge, + )) + xi_edge = np.concatenate(( + [xi_min] * n_edge, + np.linspace(xi_min, xi_max, n_edge), + [xi_max] * n_edge, + np.linspace(xi_min, xi_max, n_edge), + )) x_edge = self.xilam2x(xi_edge, wave_edge) y_edge = self.xilam2y(xi_edge, wave_edge) From 088a87be1c0d22eee0aa04c3f02021be59856ab0 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Sat, 29 Aug 2026 23:19:01 +0200 Subject: [PATCH 56/68] Skip empty FOVs --- scopesim/optics/image_plane.py | 3 ++- scopesim/optics/optical_train.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/scopesim/optics/image_plane.py b/scopesim/optics/image_plane.py index 8a4205a82..e2b101f48 100644 --- a/scopesim/optics/image_plane.py +++ b/scopesim/optics/image_plane.py @@ -115,11 +115,12 @@ def add(self, hdus, sub_pixel=None, spline_order=None, spline_order = from_currsys("!SIM.computing.spline_order", self.cmds) if isinstance(hdus, (list, tuple)): - logger.debug("Adding multiple HDUs to ImagePlane.") + logger.info("Adding multiple HDUs to ImagePlane.") for hdu in hdus: self.add(hdu, sub_pixel, spline_order, wcs_suffix) else: if not isinstance(hdus, fits.ImageHDU): + logger.error("tried to add %s", type(hdus)) raise TypeError("Only ImageHDUs may be added to ImagePlane.") logger.debug("Adding HDU with shape %d to ImagePlane.", diff --git a/scopesim/optics/optical_train.py b/scopesim/optics/optical_train.py index 46693e2a0..00823c7d8 100644 --- a/scopesim/optics/optical_train.py +++ b/scopesim/optics/optical_train.py @@ -289,6 +289,10 @@ def observe(self, orig_source=None, update=True, **kwargs): desc=" FOV effects", position=1):#, leave=False): fov = effect.apply_to(fov) + if fov.hdu is None: + logger.info(" Skipping empty FOV") + continue + if self.cmds.get("!INST.flatten", True): fov.flatten() self.image_planes[fov.image_plane_id].add(fov.hdu, wcs_suffix="D") From 45a77f644b80929ea2c576576c1083b55bbfa9b0 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Wed, 6 May 2026 15:39:36 +0200 Subject: [PATCH 57/68] Refactor `PSF.apply_to()` - Extract actual array operation (useful for JAX in the future) - Make actual apply_to easier to read (will be called by Effect mixins) - Slightly refactor toaster's shortcut from #973 --- scopesim/effects/psfs/discrete.py | 3 +- scopesim/effects/psfs/psf_base.py | 176 +++++++++++++++--------------- 2 files changed, 92 insertions(+), 87 deletions(-) diff --git a/scopesim/effects/psfs/discrete.py b/scopesim/effects/psfs/discrete.py index ded2d247f..3e85adf9c 100644 --- a/scopesim/effects/psfs/discrete.py +++ b/scopesim/effects/psfs/discrete.py @@ -7,8 +7,7 @@ import numpy as np from tqdm.auto import tqdm from scipy.signal import convolve -from scipy.ndimage import zoom -from scipy.interpolate import griddata, RectBivariateSpline, RegularGridInterpolator +from scipy.interpolate import RectBivariateSpline, griddata from astropy import units as u from astropy.io import fits diff --git a/scopesim/effects/psfs/psf_base.py b/scopesim/effects/psfs/psf_base.py index 1912d66cf..669759628 100644 --- a/scopesim/effects/psfs/psf_base.py +++ b/scopesim/effects/psfs/psf_base.py @@ -57,98 +57,104 @@ def __init__(self, **kwargs): self.meta = from_currsys(self.meta, self.cmds) self.convolution_classes = (FieldOfView, ImagePlane) + def __call__(self, data: np.ndarray, kernel: np.ndarray) -> np.ndarray: + # subtract background level before convolving, re-add afterwards + bkg_level = get_bkg_level(data, self.meta["bkg_width"]) + + if data.ndim == 3: + bkg_level = bkg_level[:, None, None] + + image = np.asarray(data - bkg_level) + + # Shortcut for flat images, which wouldn't change with convolution + if image.sum() == 0.: + logger.debug( + "%s: uniform field, convolution skipped", self.meta["name"]) + return data # return unchanged + + mode = from_currsys(self.meta["convolve_mode"], self.cmds) + + match image.ndim, kernel.ndim: + case 2, 2: + return convolve(image, kernel, mode=mode) + bkg_level + + case 3, 2: + kernel = kernel[None, ...] + return convolve(image, kernel, mode=mode) + bkg_level + + case 3, 3: + if mode != "same": + logger.warning( + "cube-cube convolution assumes mode='same', but mode " + "is %s, result may be inconsistent.", mode) + + convolved = np.zeros(data.shape) # assumes mode="same" + for iplane in range(data.shape[0]): + convolved[iplane,] = convolve( + image[iplane,], + kernel[iplane,], + mode=mode, + ) + return convolved + bkg_level + + case _: + raise ValueError("Image and Kernel shape mismatch.") + + def _apply_to_fvl(self, fvl: FovVolumeList) -> None: + logger.debug("Executing %s, FoV setup", self.meta["name"]) + if len(self._waveset) != 0: + waveset_edges = 0.5 * (self._waveset[:-1] + self._waveset[1:]) + fvl.split("wave", quantify(waveset_edges, u.um).value) + + def _apply_convolution(self, obj) -> None: + logger.debug("Executing %s, convolution", self.meta["name"]) + if ((hasattr(obj, "fields") and len(obj.fields) > 0) or + (obj.hdu is not None)): + kernel = self.get_kernel(obj).astype(float) + + # This doesn't work because of a "Delta PSF" in some mocks... + # if kernel.size == 1: # only 1 pixel + # raise ValueError("Cannot convolve single pixel PSF.") + + # apply rotational blur for field-tracking observations + rot_blur_angle = self.meta["rotational_blur_angle"] + if abs(rot_blur_angle << u.deg) > 0*u.deg: + # makes a copy of kernel + kernel = rotational_blur(kernel, rot_blur_angle) + + # Round the edges of kernels so that the silly square stars + # don't appear anymore + if self.meta.get("rounded_edges", False) and kernel.ndim == 2: + kernel = self._round_kernel_edges(kernel) + + # normalise psf kernel KERNEL SHOULD BE normalised within get_kernel() + # if from_currsys(self.meta["normalise_kernel"], self.cmds): + # kernel /= np.sum(kernel) + # kernel[kernel < 0.] = 0. + + orig_shape = obj.hdu.data.shape + + logger.debug("PSF convolution start") + obj.hdu.data = self(obj.hdu.data.astype(float), kernel) + logger.debug("PSF convolution done") + + # TODO: careful with which dimensions mean what + d_x = obj.hdu.data.shape[-1] - orig_shape[-1] + d_y = obj.hdu.data.shape[-2] - orig_shape[-2] + for wcsid in ["", "D"]: + if "CRPIX1" + wcsid in obj.hdu.header: + obj.hdu.header["CRPIX1" + wcsid] += d_x / 2 + obj.hdu.header["CRPIX2" + wcsid] += d_y / 2 + def apply_to(self, obj, **kwargs): """Apply the PSF.""" # 1. During setup of the FieldOfViews if isinstance(obj, FovVolumeList) and self._waveset is not None: - logger.debug("Executing %s, FoV setup", self.meta['name']) - waveset = self._waveset - if len(waveset) != 0: - waveset_edges = 0.5 * (waveset[:-1] + waveset[1:]) - obj.split("wave", quantify(waveset_edges, u.um).value) + self._apply_to_fvl(obj) # 2. During observe: convolution elif isinstance(obj, self.convolution_classes): - logger.debug("Executing %s, convolution", self.meta['name']) - if ((hasattr(obj, "fields") and len(obj.fields) > 0) or - (obj.hdu is not None)): - image = obj.hdu.data.astype(float) - - # subtract background level before convolving, re-add afterwards - bkg_level = get_bkg_level(image, self.meta["bkg_width"]) - - # Short-circuit for spatially uniform fields (e.g. darks and - # closed-shutter calibration frames): the background is - # subtracted before the convolution below and re-added after, - # so for a uniform image the convolution input is identically - # zero and the output is bit-for-bit the input. Skip the - # (expensive) kernel interpolation/rescale and FFT entirely. - # This is exact, not approximate: it only fires when the - # residual is exactly zero everywhere. - # np.asarray: the FOV data can be an astropy Quantity, whose - # truthiness/.any() raises TypeError; we only need the values - if image.ndim == 3: - residual = np.asarray( - image - np.asarray(bkg_level)[:, None, None]) - else: - residual = np.asarray(image - bkg_level) - if not residual.any(): - logger.debug( - "%s: uniform field, convolution skipped", - self.meta["name"]) - obj.hdu.data = image - return obj - - kernel = self.get_kernel(obj).astype(float) - - # This doesn't work because of a "Delta PSF" in some mocks... - # if kernel.size == 1: # only 1 pixel - # raise ValueError("Cannot convolve single pixel PSF.") - - # apply rotational blur for field-tracking observations - rot_blur_angle = self.meta["rotational_blur_angle"] - if abs(rot_blur_angle << u.deg) > 0*u.deg: - # makes a copy of kernel - kernel = rotational_blur(kernel, rot_blur_angle) - - # Round the edges of kernels so that the silly square stars - # don't appear anymore - if self.meta.get("rounded_edges", False) and kernel.ndim == 2: - kernel = self._round_kernel_edges(kernel) - - # normalise psf kernel KERNEL SHOULD BE normalised within get_kernel() - # if from_currsys(self.meta["normalise_kernel"], self.cmds): - # kernel /= np.sum(kernel) - # kernel[kernel < 0.] = 0. - - # do the convolution - mode = from_currsys(self.meta["convolve_mode"], self.cmds) - - logger.debug("PSF convolution start") - if image.ndim == 2 and kernel.ndim == 2: - new_image = convolve(image - bkg_level, kernel, mode=mode) - elif image.ndim == 3 and kernel.ndim == 2: - kernel = kernel[None, :, :] - bkg_level = bkg_level[:, None, None] - new_image = convolve(image - bkg_level, kernel, mode=mode) - elif image.ndim == 3 and kernel.ndim == 3: - bkg_level = bkg_level[:, None, None] - new_image = np.zeros(image.shape) # assumes mode="same" - for iplane in range(image.shape[0]): - new_image[iplane,] = convolve( - image[iplane,] - bkg_level[iplane,], - kernel[iplane,], mode=mode) - - obj.hdu.data = new_image + bkg_level - logger.debug("PSF convolution done") - - # TODO: careful with which dimensions mean what - d_x = new_image.shape[-1] - image.shape[-1] - d_y = new_image.shape[-2] - image.shape[-2] - for wcsid in ["", "D"]: - if "CRPIX1" + wcsid in obj.hdu.header: - obj.hdu.header["CRPIX1" + wcsid] += d_x / 2 - obj.hdu.header["CRPIX2" + wcsid] += d_y / 2 + self._apply_convolution(obj) return obj From 3289860902a7d57cc6c17150efc917a24b6b37fe Mon Sep 17 00:00:00 2001 From: teutoburg Date: Sun, 30 Aug 2026 23:03:24 +0200 Subject: [PATCH 58/68] Cleanup PSF Leftovers from #1002 --- scopesim/effects/psfs/psf_base.py | 94 ++++++++++++++++--------------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/scopesim/effects/psfs/psf_base.py b/scopesim/effects/psfs/psf_base.py index 669759628..0216a75a4 100644 --- a/scopesim/effects/psfs/psf_base.py +++ b/scopesim/effects/psfs/psf_base.py @@ -4,6 +4,7 @@ from typing import ClassVar import numpy as np +from numpy.typing import ArrayLike, NDArray from scipy.signal import convolve from scipy.ndimage import rotate from astropy import units as u @@ -57,7 +58,7 @@ def __init__(self, **kwargs): self.meta = from_currsys(self.meta, self.cmds) self.convolution_classes = (FieldOfView, ImagePlane) - def __call__(self, data: np.ndarray, kernel: np.ndarray) -> np.ndarray: + def __call__(self, data: ArrayLike, kernel: ArrayLike) -> NDArray: # subtract background level before convolving, re-add afterwards bkg_level = get_bkg_level(data, self.meta["bkg_width"]) @@ -69,7 +70,7 @@ def __call__(self, data: np.ndarray, kernel: np.ndarray) -> np.ndarray: # Shortcut for flat images, which wouldn't change with convolution if image.sum() == 0.: logger.debug( - "%s: uniform field, convolution skipped", self.meta["name"]) + "%s: uniform field, convolution skipped", self.display_name) return data # return unchanged mode = from_currsys(self.meta["convolve_mode"], self.cmds) @@ -101,50 +102,55 @@ def __call__(self, data: np.ndarray, kernel: np.ndarray) -> np.ndarray: raise ValueError("Image and Kernel shape mismatch.") def _apply_to_fvl(self, fvl: FovVolumeList) -> None: - logger.debug("Executing %s, FoV setup", self.meta["name"]) - if len(self._waveset) != 0: - waveset_edges = 0.5 * (self._waveset[:-1] + self._waveset[1:]) - fvl.split("wave", quantify(waveset_edges, u.um).value) + logger.debug("Apply %s, FoV setup", self.display_name) + + if len(self._waveset) == 0: + return + + waveset_edges = 0.5 * (self._waveset[:-1] + self._waveset[1:]) + fvl.split("wave", quantify(waveset_edges, u.um).value) def _apply_convolution(self, obj) -> None: - logger.debug("Executing %s, convolution", self.meta["name"]) - if ((hasattr(obj, "fields") and len(obj.fields) > 0) or - (obj.hdu is not None)): - kernel = self.get_kernel(obj).astype(float) - - # This doesn't work because of a "Delta PSF" in some mocks... - # if kernel.size == 1: # only 1 pixel - # raise ValueError("Cannot convolve single pixel PSF.") - - # apply rotational blur for field-tracking observations - rot_blur_angle = self.meta["rotational_blur_angle"] - if abs(rot_blur_angle << u.deg) > 0*u.deg: - # makes a copy of kernel - kernel = rotational_blur(kernel, rot_blur_angle) - - # Round the edges of kernels so that the silly square stars - # don't appear anymore - if self.meta.get("rounded_edges", False) and kernel.ndim == 2: - kernel = self._round_kernel_edges(kernel) - - # normalise psf kernel KERNEL SHOULD BE normalised within get_kernel() - # if from_currsys(self.meta["normalise_kernel"], self.cmds): - # kernel /= np.sum(kernel) - # kernel[kernel < 0.] = 0. - - orig_shape = obj.hdu.data.shape - - logger.debug("PSF convolution start") - obj.hdu.data = self(obj.hdu.data.astype(float), kernel) - logger.debug("PSF convolution done") - - # TODO: careful with which dimensions mean what - d_x = obj.hdu.data.shape[-1] - orig_shape[-1] - d_y = obj.hdu.data.shape[-2] - orig_shape[-2] - for wcsid in ["", "D"]: - if "CRPIX1" + wcsid in obj.hdu.header: - obj.hdu.header["CRPIX1" + wcsid] += d_x / 2 - obj.hdu.header["CRPIX2" + wcsid] += d_y / 2 + logger.debug("Apply %s, convolution", self.display_name) + + if ((not hasattr(obj, "fields") or len(obj.fields) == 0) + and (obj.hdu is None)): + return + + kernel = self.get_kernel(obj).astype(float) + + # This doesn't work because of a "Delta PSF" in some mocks... + # if kernel.size == 1: # only 1 pixel + # raise ValueError("Cannot convolve single pixel PSF.") + + # apply rotational blur for field-tracking observations + rot_blur_angle = self.meta["rotational_blur_angle"] + if abs(rot_blur_angle << u.deg) > 0*u.deg: + # makes a copy of kernel + kernel = rotational_blur(kernel, rot_blur_angle) + + # Round the edges of kernels to avoid square stars + if self.meta.get("rounded_edges", False) and kernel.ndim == 2: + kernel = self._round_kernel_edges(kernel) + + # normalise psf kernel KERNEL SHOULD BE normalised within get_kernel() + # if from_currsys(self.meta["normalise_kernel"], self.cmds): + # kernel /= np.sum(kernel) + # kernel[kernel < 0.] = 0. + + orig_shape = obj.hdu.data.shape + + logger.debug("PSF convolution start") + obj.hdu.data = self(obj.hdu.data.astype(float), kernel) + logger.debug("PSF convolution done") + + # TODO: careful with which dimensions mean what + d_x = obj.hdu.data.shape[-1] - orig_shape[-1] + d_y = obj.hdu.data.shape[-2] - orig_shape[-2] + for wcsid in ["", "D"]: + if "CRPIX1" + wcsid in obj.hdu.header: + obj.hdu.header["CRPIX1" + wcsid] += d_x / 2 + obj.hdu.header["CRPIX2" + wcsid] += d_y / 2 def apply_to(self, obj, **kwargs): """Apply the PSF.""" From 68e748b2de78602072928275a86a506550bca33d Mon Sep 17 00:00:00 2001 From: teutoburg Date: Mon, 31 Aug 2026 01:20:06 +0200 Subject: [PATCH 59/68] Use correct NumPy type hints Basically: ArrayLike for input (which _might_ be an array, but could also be e.g. a list) and NDArray for output (which we know will be an array). --- scopesim/effects/illumination.py | 9 +++++---- scopesim/effects/psfs/psf_base.py | 4 ++-- scopesim/optics/image_plane_utils.py | 30 ++++++++++++++++++---------- scopesim/source/source_fields.py | 23 +++++++++++++-------- scopesim/utils.py | 5 +++-- 5 files changed, 45 insertions(+), 26 deletions(-) diff --git a/scopesim/effects/illumination.py b/scopesim/effects/illumination.py index eaa340854..adf0b8851 100644 --- a/scopesim/effects/illumination.py +++ b/scopesim/effects/illumination.py @@ -5,6 +5,7 @@ from collections.abc import Callable, Mapping import numpy as np +from numpy.typing import NDArray from astropy import units as u from astropy.modeling.functional_models import Gaussian2D @@ -22,7 +23,7 @@ def gaussian2d( mu: tuple[float, float] = (0.0, 0.0), sigma: tuple[float, float] = (2000.0, 2000.0), theta: u.Quantity[u.deg] | float = 0.0 * u.deg, -) -> np.ndarray: +) -> NDArray: """ 2D elliptical Gaussian to be used for vignetting map. @@ -45,7 +46,7 @@ def gaussian2d( Returns ------- - np.ndarray + NDArray Vignetting map. """ @@ -71,7 +72,7 @@ def quadratic_vignetting( r_ref: float | None = None, mu: tuple[float, float] = (0.0, 0.0), stretch: tuple[float, float, float, float] = (1.0, 1.0, 1.0, 1.0), -) -> np.ndarray: +) -> NDArray: """ Quadratic vignetting pattern with independent stretch factors. @@ -97,7 +98,7 @@ def quadratic_vignetting( Returns ------- - np.ndarray + NDArray Vignetting map. """ diff --git a/scopesim/effects/psfs/psf_base.py b/scopesim/effects/psfs/psf_base.py index 0216a75a4..4f617c34c 100644 --- a/scopesim/effects/psfs/psf_base.py +++ b/scopesim/effects/psfs/psf_base.py @@ -171,7 +171,7 @@ def get_kernel(self, obj): return self.kernel @staticmethod - def _round_kernel_edges(kernel: np.ndarray) -> np.ndarray: + def _round_kernel_edges(kernel: ArrayLike) -> NDArray: x, y = np.array(kernel.shape) // 2 threshold = min(kernel[x, 0], kernel[x, -1], kernel[0, y], kernel[-1, y]) @@ -206,7 +206,7 @@ def rotational_blur(image, angle: u.Quantity[u.deg]): Returns ------- - image_rot : np.ndarray + image_rot : NDArray Blurred image """ diff --git a/scopesim/optics/image_plane_utils.py b/scopesim/optics/image_plane_utils.py index b4901e70b..62f717315 100644 --- a/scopesim/optics/image_plane_utils.py +++ b/scopesim/optics/image_plane_utils.py @@ -4,6 +4,7 @@ from collections.abc import Iterable import numpy as np +from numpy.typing import ArrayLike, NDArray from astropy import units as u from astropy.wcs import WCS, find_all_wcs from astropy.io import fits @@ -154,9 +155,11 @@ def _make_bounding_header_for_tables(*tables, pixel_scale=1*u.arcsec): return hdr -def create_wcs_from_points(points: np.ndarray, - pixel_scale: float, - wcs_suffix: str = "") -> tuple[WCS, np.ndarray]: +def create_wcs_from_points( + points: ArrayLike, + pixel_scale: float, + wcs_suffix: str = "", +) -> tuple[WCS, NDArray]: """ Create `astropy.wcs.WCS` instance that fits all points inside. @@ -1017,9 +1020,14 @@ def calc_footprint(header, wcs_suffix="", new_unit: str = None): return xy1 -def calc_table_footprint(table: Table, x_name: str, y_name: str, - tbl_unit: str, new_unit: str, - padding=None) -> np.ndarray: +def calc_table_footprint( + table: Table, + x_name: str, + y_name: str, + tbl_unit: str, + new_unit: str, + padding: u.Quantity | None = None, +) -> NDArray: """ Equivalent to ``calc_footprint()``, but for tables instead of images. @@ -1130,10 +1138,12 @@ def _get_unit_from_headers(*headers, wcs_suffix: str = "") -> str: return unit -def det_wcs_from_sky_wcs(sky_wcs: WCS, - pixel_scale: float, - plate_scale: float, - naxis=None) -> tuple[WCS, np.ndarray]: +def det_wcs_from_sky_wcs( + sky_wcs: WCS, + pixel_scale: float, + plate_scale: float, + naxis: tuple[int, int] | None = None, +) -> tuple[WCS, NDArray]: """ Create detector WCS from celestial WCS using pixel and plate scales. diff --git a/scopesim/source/source_fields.py b/scopesim/source/source_fields.py index 3ca2a2c18..ecd315cf7 100644 --- a/scopesim/source/source_fields.py +++ b/scopesim/source/source_fields.py @@ -62,6 +62,7 @@ class HDUSourceField{+wcs} # rename it dataclass_field to avoid confusion with source field import numpy as np +from numpy.typing import ArrayLike, NDArray from astropy.table import Table, Column from astropy.io.registry import IORegistryError @@ -127,7 +128,7 @@ def name(self) -> str: """Name of the object (if set).""" return self.meta.get("object", "") - def get_corners(self, unit: u.Unit | str = "arcsec") -> np.ndarray: + def get_corners(self, unit: u.Unit | str = "arcsec") -> NDArray: """Calculate and return footprint corner points in `unit`. .. versionadded:: 0.10.0 @@ -187,9 +188,15 @@ def from_file(cls, filename: Path | str, return cls(tbl, spectra=spectra, meta=kwargs) @classmethod - def from_arrays(cls, x, y, ref, weight, - spectra: dict[int, SourceSpectrum], - **kwargs): + def from_arrays( + cls, + x: ArrayLike, + y: ArrayLike, + ref: ArrayLike, + weight: ArrayLike, + spectra: dict[int, SourceSpectrum], + **kwargs, + ): """Construct source table from arrays for each column.""" if weight is None: weight = np.ones(len(x)) @@ -222,7 +229,7 @@ def _write_stream(self, stream: TextIO) -> None: stream.write(f"Table with {len(self)} rows, referencing " f"spectra {set(self.spectra)}") - def get_corners(self, unit: u.Unit | str = "arcsec") -> np.ndarray: + def get_corners(self, unit: u.Unit | str = "arcsec") -> NDArray: """Calculate and return footprint corner points in `unit`.""" x_qty = self.field["x"].quantity.to(unit).value y_qty = self.field["y"].quantity.to(unit).value @@ -283,7 +290,7 @@ def header(self) -> fits.Header: return self.field.header @property - def data(self) -> np.ndarray: + def data(self) -> NDArray: """Shortcut for `field.data`.""" return self.field.data @@ -333,7 +340,7 @@ def _write_stream(self, stream: TextIO) -> None: stream.write(f"ImageHDU with size {self.img_size}, referencing " f"spectrum {self.field.header.get('SPEC_REF', '-')}") - def get_corners(self, unit: u.Unit | str = "arcsec") -> np.ndarray: + def get_corners(self, unit: u.Unit | str = "arcsec") -> NDArray: """Calculate and return footprint corner points in `unit`.""" return imp_utils.calc_footprint(self.header, new_unit=unit) @@ -445,7 +452,7 @@ class BackgroundSourceField(SpectrumSourceField): header: fits.Header - def get_corners(self, unit: u.Unit | str = "arcsec") -> np.ndarray: + def get_corners(self, unit: u.Unit | str = "arcsec") -> NDArray: """Return imaginary corner from + to - infinity.""" return np.array([-np.inf, np.inf]) diff --git a/scopesim/utils.py b/scopesim/utils.py index f81536864..a22c19ae7 100644 --- a/scopesim/utils.py +++ b/scopesim/utils.py @@ -15,6 +15,7 @@ from docutils.core import publish_string import yaml import numpy as np +from numpy.typing import NDArray import matplotlib as mpl from matplotlib import pyplot as plt from astropy import units as u @@ -350,7 +351,7 @@ def zeros_from_header( header: fits.Header, dtype: type | np.dtype = float, ndims: int | None = None, -) -> np.ndarray: +) -> NDArray: """ Create all-zero array of the shape given by NAXISn keywords in `header`. @@ -369,7 +370,7 @@ def zeros_from_header( Returns ------- - arr : np.ndarray + arr : NDArray All-zero array of desired shape and dtype. """ From a16aa59212a63af223cf59b5c978d83b0b1106ec Mon Sep 17 00:00:00 2001 From: oczoske Date: Tue, 1 Sep 2026 16:26:36 +0200 Subject: [PATCH 60/68] Amend tests for N band laser --- scopesim/tests/tests_effects/test_MetisWCU.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scopesim/tests/tests_effects/test_MetisWCU.py b/scopesim/tests/tests_effects/test_MetisWCU.py index 8c8f63d47..d97c92726 100644 --- a/scopesim/tests/tests_effects/test_MetisWCU.py +++ b/scopesim/tests/tests_effects/test_MetisWCU.py @@ -47,6 +47,8 @@ def fixture_bbsource(): laser_t_power=70e-3, # [W] laser_m_wave=5.26, # [um] laser_m_power=20e-3, # [W] + laser_n_wave=10.4, # [um] + laser_n_power=2.e-3, # [W] bb_aperture=1., bb_to_is=None, rho_tube=0.95, From 1e4c727fb789347de0a878bdced9cca585070806 Mon Sep 17 00:00:00 2001 From: oczoske Date: Tue, 1 Sep 2026 17:24:45 +0200 Subject: [PATCH 61/68] Remove stray print statements --- scopesim/effects/metis_wcu/metis_wcu.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/scopesim/effects/metis_wcu/metis_wcu.py b/scopesim/effects/metis_wcu/metis_wcu.py index 810bb97d3..725ed9230 100644 --- a/scopesim/effects/metis_wcu/metis_wcu.py +++ b/scopesim/effects/metis_wcu/metis_wcu.py @@ -490,9 +490,7 @@ def _laser_intensity(self): self.meta['laser_n_wave'] = [self.meta['laser_n_wave']] if not isinstance(self.meta['laser_n_power'], Iterable): self.meta['laser_n_power'] = [self.meta['laser_n_power']] - print(self.meta['laser_n_wave']) lamc_n = self.meta["laser_n_wave"] * u.um - print(self.meta['laser_n_power']) power_n = self.meta["laser_n_power"] * u.W / (c.c * c.h / lamc_n) * u.ph @@ -503,7 +501,6 @@ def _laser_intensity(self): power_n *= self.fibre_trans sigma = 2 * dlam - print(sigma) amp = 1/(sigma * np.sqrt(2 * np.pi)) line_l = Gaussian1D(amplitude=amp, mean=lamc_l, stddev=sigma) From a9091fb53ad7f158e4523b02f743c621792c91be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2026 15:45:49 +0000 Subject: [PATCH 62/68] Bump tqdm from 4.68.4 to 4.70.0 Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.68.4 to 4.70.0. - [Release notes](https://github.com/tqdm/tqdm/releases) - [Commits](https://github.com/tqdm/tqdm/compare/v4.68.4...v4.70.0) --- updated-dependencies: - dependency-name: tqdm dependency-version: 4.70.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 10 +++++----- pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3cd37f028..fb9aa367a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2975,7 +2975,7 @@ files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] -markers = {dev = "os_name != \"nt\" or sys_platform != \"win32\"", docs = "sys_platform != \"win32\"", test = "sys_platform != \"win32\""} +markers = {dev = "sys_platform != \"win32\" or os_name != \"nt\"", docs = "sys_platform != \"win32\"", test = "sys_platform != \"win32\""} [[package]] name = "pure-eval" @@ -4246,14 +4246,14 @@ files = [ [[package]] name = "tqdm" -version = "4.68.4" +version = "4.70.0" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "tqdm-4.68.4-py3-none-any.whl", hash = "sha256:5168118b2368f48c561afda8020fd79195b1bdb0bdf8086b88442c267a315dc2"}, - {file = "tqdm-4.68.4.tar.gz", hash = "sha256:19829c9673638f2a0b8617da4cdcb927e831cd88bcfcb6e78d42a4d1af131520"}, + {file = "tqdm-4.70.0-py3-none-any.whl", hash = "sha256:7f585706bfddbdebf89daac705b2dfcc16890130727d3197ca62c732b4310953"}, + {file = "tqdm-4.70.0.tar.gz", hash = "sha256:55b0b0dbd97462d06ebee91e4dac24ed4d4702be82b24f07e6c1d27e08cea220"}, ] [package.dependencies] @@ -4426,4 +4426,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.12,<3.15" -content-hash = "720a1af3697025b6b41b99e33093188b5450350c536b7b1f57bed5efe78bc52d" +content-hash = "e99f4737f1d271754494ecd15b2bf8014c8075a8a802ea2c2e8f8fcf4d86f091" diff --git a/pyproject.toml b/pyproject.toml index 942aa42f4..326248146 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ dependencies = [ "lxml (>=6.1.1,<7.0.0)", "pyyaml (>=6.0.3,<7.0.0)", "more-itertools (>=10.2.0,<11.0.0)", - "tqdm (>=4.67.4,<5.0.0)", + "tqdm (>=4.70.0,<5.0.0)", "packaging (>=26.0,<28.0)", "synphot (>=1.7.0,<2.0.0)", From b7e40133febf1b7f26239494aef8561869454e2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2026 21:37:00 +0000 Subject: [PATCH 63/68] Bump tornado from 6.5.7 to 6.5.8 Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.5.7 to 6.5.8. - [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst) - [Commits](https://github.com/tornadoweb/tornado/compare/v6.5.7...v6.5.8) --- updated-dependencies: - dependency-name: tornado dependency-version: 6.5.8 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- poetry.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3cd37f028..72411e815 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2975,7 +2975,7 @@ files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] -markers = {dev = "os_name != \"nt\" or sys_platform != \"win32\"", docs = "sys_platform != \"win32\"", test = "sys_platform != \"win32\""} +markers = {dev = "sys_platform != \"win32\" or os_name != \"nt\"", docs = "sys_platform != \"win32\"", test = "sys_platform != \"win32\""} [[package]] name = "pure-eval" @@ -4226,22 +4226,22 @@ test = ["flake8", "isort", "pytest"] [[package]] name = "tornado" -version = "6.5.7" +version = "6.5.8" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.9" groups = ["dev", "docs", "test"] files = [ - {file = "tornado-6.5.7-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:148b2eb15c2c765a50796172c1e499649b35f30d2e3c3d3e15913cfa56bfb163"}, - {file = "tornado-6.5.7-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9da38de27f1da3b78a966f0dae12b5a1ea9afe72ca805d84ff06508272ddf100"}, - {file = "tornado-6.5.7-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8d759e71906ee783f8867b93bf26a265743da4c1e2f4a018464c1ba019862972"}, - {file = "tornado-6.5.7-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a46347a18f23fb92b396beebe0fb78f61dda0cc302445202c16203d8a18848b"}, - {file = "tornado-6.5.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7778b30bef919231265e91c69963ce0f49a1e9c07ac900bbe75b19ce2575ba92"}, - {file = "tornado-6.5.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e726f0c75da7726eec023aa62751ff8878bd2737e34fbdd33b1ae5897d2200f5"}, - {file = "tornado-6.5.7-cp39-abi3-win32.whl", hash = "sha256:f8de3bf12d3efdd0cbe7c8887868198f8a91415e3f29fcf258d9b8eb7b1d9ae4"}, - {file = "tornado-6.5.7-cp39-abi3-win_amd64.whl", hash = "sha256:de942f843533a039ef9fa3d9c88c7cd8a7c94553fb5ad0154270989b3d99a2c4"}, - {file = "tornado-6.5.7-cp39-abi3-win_arm64.whl", hash = "sha256:ff934fce95643af5f11efdae618eaa73d469dc588641e5c8d19295a0c65c4796"}, - {file = "tornado-6.5.7.tar.gz", hash = "sha256:66c513a76cda70d53907bc27cf1447557699c2e95aa48ba27a442ff61c3ddfc2"}, + {file = "tornado-6.5.8-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:cc6aa787d7cfab7c3d35189dc7a56fbd2399a569624c730c6b55b3d6531d0403"}, + {file = "tornado-6.5.8-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9715b5eb79735b2bcd454ce216a9275b7c0470e64ea1bf5742f78b2f72b26eeb"}, + {file = "tornado-6.5.8-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:547d63f450d570c14fe0e8db2cfb14c9bbd1c2503b4a6612586267955aa47b58"}, + {file = "tornado-6.5.8-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e2360a0ffbe145eca8af0b19cb7203d79b1a98dd4cccdd6b368f6f49c2e3808"}, + {file = "tornado-6.5.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5d242290bdf7ab3151bc1065fdd75c0dcc21cbc7b49f22a4c56329c2d6566d22"}, + {file = "tornado-6.5.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7b94ff0e128fe0542f3bd331fb44d06260fc4ac16881545159f34ef08aad4195"}, + {file = "tornado-6.5.8-cp39-abi3-win32.whl", hash = "sha256:67832909c4779c64942380cb5f044a5c6163d00831472d80e25e115de9917836"}, + {file = "tornado-6.5.8-cp39-abi3-win_amd64.whl", hash = "sha256:11881db6b7c168494be2c2d12e65931451bdf7ee718535418ae1d8855dd5a0ee"}, + {file = "tornado-6.5.8-cp39-abi3-win_arm64.whl", hash = "sha256:68a7468c7e289f8514d7d664101753903217eff1bb6822c6b5994a0b5f5bcb26"}, + {file = "tornado-6.5.8.tar.gz", hash = "sha256:9452e1b208a8bd771e2cb1f2ff564985b9b214bdebbe622793e1799e0a6bd23f"}, ] [[package]] From a9438bb9b9b6b4430ed51b017ab6776266e8286d Mon Sep 17 00:00:00 2001 From: "astarvienna-bot[bot]" <217837468+myciapp[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 00:41:28 +0000 Subject: [PATCH 64/68] Bumping version from 0.12.0b2.dev2 to 0.12.0b2.dev3 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 942aa42f4..a9697c722 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ScopeSim" -version = "0.12.0b2.dev2" +version = "0.12.0b2.dev3" description = "Generalised telescope observation simulator" license = {text = "GPL-3.0-or-later"} authors = [ From 21bfb8ae1b5cdaf00c1df304031d6dbbf2b9c3cc Mon Sep 17 00:00:00 2001 From: "astarvienna-bot[bot]" <217837468+myciapp[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 00:51:40 +0000 Subject: [PATCH 65/68] Bumping version from 0.12.0b2.dev3 to 0.12.0b2.dev4 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 62a001d3e..43294fa45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ScopeSim" -version = "0.12.0b2.dev3" +version = "0.12.0b2.dev4" description = "Generalised telescope observation simulator" license = {text = "GPL-3.0-or-later"} authors = [ From 6a0c34b74850bb94b9f75eab9547d196535d2734 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 11:43:48 +0000 Subject: [PATCH 66/68] Bump the dev-dependencies group across 1 directory with 4 updates Bumps the dev-dependencies group with 4 updates in the / directory: [jupytext](https://github.com/jupytext/jupytext), [pytest](https://github.com/pytest-dev/pytest), [sphinx-book-theme](https://github.com/executablebooks/sphinx-book-theme) and [sphinxcontrib-mermaid](https://github.com/mgaitan/sphinxcontrib-mermaid). Updates `jupytext` from 1.19.3 to 1.19.5 - [Release notes](https://github.com/jupytext/jupytext/releases) - [Changelog](https://github.com/jupytext/jupytext/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupytext/jupytext/compare/v1.19.3...v1.19.5) Updates `pytest` from 9.0.3 to 9.1.1 - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/9.0.3...9.1.1) Updates `sphinx-book-theme` from 1.1.4 to 1.4.0 - [Release notes](https://github.com/executablebooks/sphinx-book-theme/releases) - [Changelog](https://github.com/executablebooks/sphinx-book-theme/blob/main/CHANGELOG.md) - [Commits](https://github.com/executablebooks/sphinx-book-theme/compare/v1.1.4...v1.4.0) Updates `sphinxcontrib-mermaid` from 2.0.2 to 2.1.0 - [Changelog](https://github.com/mgaitan/sphinxcontrib-mermaid/blob/master/CHANGELOG.md) - [Commits](https://github.com/mgaitan/sphinxcontrib-mermaid/compare/2.0.2...2.1.0) --- updated-dependencies: - dependency-name: jupytext dependency-version: 1.19.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies - dependency-name: pytest dependency-version: 9.1.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: sphinx-book-theme dependency-version: 1.4.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: sphinxcontrib-mermaid dependency-version: 2.1.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies ... Signed-off-by: dependabot[bot] --- poetry.lock | 54 +++++++++++++++++++++++++------------------------- pyproject.toml | 8 ++++---- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/poetry.lock b/poetry.lock index 77bbd8770..ebb77fc36 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1818,14 +1818,14 @@ files = [ [[package]] name = "jupytext" -version = "1.19.3" +version = "1.19.5" description = "Jupyter notebooks as Markdown documents, Julia, Python or R scripts" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "jupytext-1.19.3-py3-none-any.whl", hash = "sha256:acf75492f80895ad8e664fd8db1708b617008dd0e71c341a1abc3d0d07310ed0"}, - {file = "jupytext-1.19.3.tar.gz", hash = "sha256:713c3ed4441afe0f31474d28ea2e6b61a268c04c40fd78e5ccfd7f7ac9e9f766"}, + {file = "jupytext-1.19.5-py3-none-any.whl", hash = "sha256:af22351202171116b986fe1f4f9233a13ca4a85d5eec57a87900ed48521dbae4"}, + {file = "jupytext-1.19.5.tar.gz", hash = "sha256:605026446d605aa54fd7f7fc69df6ae51c7a46053d4cebf05afdc64d66de3df0"}, ] [package.dependencies] @@ -3007,14 +3007,14 @@ markers = {dev = "implementation_name != \"PyPy\"", docs = "implementation_name [[package]] name = "pydata-sphinx-theme" -version = "0.15.4" +version = "0.20.0" description = "Bootstrap-based Sphinx theme from the PyData community" optional = false -python-versions = ">=3.9" +python-versions = ">=3.11" groups = ["docs"] files = [ - {file = "pydata_sphinx_theme-0.15.4-py3-none-any.whl", hash = "sha256:2136ad0e9500d0949f96167e63f3e298620040aea8f9c74621959eda5d4cf8e6"}, - {file = "pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d"}, + {file = "pydata_sphinx_theme-0.20.0-py3-none-any.whl", hash = "sha256:56744483c9d72c783e075de716ab95d486108b69605df7528078090b73f11f69"}, + {file = "pydata_sphinx_theme-0.20.0.tar.gz", hash = "sha256:0da172d41e19a66de875f4002f7054b385372ec65763852193791e658d50bb4a"}, ] [package.dependencies] @@ -3022,17 +3022,17 @@ accessible-pygments = "*" Babel = "*" beautifulsoup4 = "*" docutils = "!=0.17.0" -packaging = "*" +jinja2 = "*" pygments = ">=2.7" -sphinx = ">=5" -typing-extensions = "*" +requests = "*" +sphinx = ">=8.2,<10" [package.extras] -a11y = ["pytest-playwright"] +a11y = ["pydata-sphinx-theme[test]"] dev = ["pandoc", "pre-commit", "pydata-sphinx-theme[doc,test]", "pyyaml", "sphinx-theme-builder[cli]", "tox"] -doc = ["ablog (>=0.11.8)", "colorama", "graphviz", "ipykernel", "ipyleaflet", "ipywidgets", "jupyter_sphinx", "jupyterlite-sphinx", "linkify-it-py", "matplotlib", "myst-parser", "nbsphinx", "numpy", "numpydoc", "pandas", "plotly", "rich", "sphinx-autoapi (>=3.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-favicon (>=1.0.1)", "sphinx-sitemap", "sphinx-togglebutton", "sphinxcontrib-youtube (>=1.4.1)", "sphinxext-rediraffe", "xarray"] +doc = ["ablog (>=0.11.8)", "astroid (>=3,!=4.0.3)", "colorama", "graphviz", "ipykernel", "ipyleaflet", "ipywidgets", "jupyter_sphinx", "jupyterlite-sphinx", "linkify-it-py", "matplotlib", "myst-parser", "nbsphinx", "numpy", "numpydoc", "pandas", "plotly", "rich", "sphinx-autoapi (==3.6.1)", "sphinx-copybutton", "sphinx-design", "sphinx-favicon (>=1.0.1)", "sphinx-sitemap (<2.7.0)", "sphinx-togglebutton", "sphinxcontrib-mermaid", "sphinxcontrib-youtube (>=1.4.1)", "sphinxext-rediraffe", "xarray"] i18n = ["Babel", "jinja2"] -test = ["pytest", "pytest-cov", "pytest-regressions", "sphinx[test]"] +test = ["pytest", "pytest-cov", "pytest-playwright", "pytest-regressions", "sphinx[test]", "typing-extensions"] [[package]] name = "pyerfa" @@ -3094,14 +3094,14 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" -version = "9.0.3" +version = "9.1.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.10" groups = ["test"] files = [ - {file = "pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9"}, - {file = "pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c"}, + {file = "pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c"}, + {file = "pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313"}, ] [package.dependencies] @@ -3840,23 +3840,23 @@ sphinxcontrib-serializinghtml = ">=1.1.9" [[package]] name = "sphinx-book-theme" -version = "1.1.4" +version = "1.4.0" description = "A clean book theme for scientific explanations and documentation with Sphinx" optional = false -python-versions = ">=3.9" +python-versions = ">=3.11" groups = ["docs"] files = [ - {file = "sphinx_book_theme-1.1.4-py3-none-any.whl", hash = "sha256:843b3f5c8684640f4a2d01abd298beb66452d1b2394cd9ef5be5ebd5640ea0e1"}, - {file = "sphinx_book_theme-1.1.4.tar.gz", hash = "sha256:73efe28af871d0a89bd05856d300e61edce0d5b2fbb7984e84454be0fedfe9ed"}, + {file = "sphinx_book_theme-1.4.0-py3-none-any.whl", hash = "sha256:10fd57d667ee0b565928c3ce95562e281c5d1b94bd5a2aafc5cd41a5d498dcd0"}, + {file = "sphinx_book_theme-1.4.0.tar.gz", hash = "sha256:fee3cd70573d9a8cd4ab380a3cb1f11139c24dcaa2cf47d06d7f924f1b44eaff"}, ] [package.dependencies] -pydata-sphinx-theme = "0.15.4" -sphinx = ">=6.1" +pydata-sphinx-theme = "0.20.0" +sphinx = ">=8.2" [package.extras] code-style = ["pre-commit"] -doc = ["ablog", "folium", "ipywidgets", "matplotlib", "myst-nb", "nbclient", "numpy", "numpydoc", "pandas", "plotly", "sphinx-copybutton", "sphinx-design", "sphinx-examples", "sphinx-tabs", "sphinx-thebe", "sphinx-togglebutton", "sphinxcontrib-bibtex", "sphinxcontrib-youtube", "sphinxext-opengraph"] +doc = ["ablog (>=0.11.13)", "folium", "ipywidgets", "matplotlib", "myst-nb", "nbclient", "numpy", "numpydoc", "pandas", "plotly", "sphinx-copybutton", "sphinx-design", "sphinx-examples", "sphinx-tabs", "sphinx-thebe", "sphinx-togglebutton", "sphinxcontrib-bibtex", "sphinxcontrib-youtube", "sphinxext-opengraph"] test = ["beautifulsoup4", "coverage", "defusedxml", "myst-nb", "pytest", "pytest-cov", "pytest-regressions", "sphinx_thebe"] [[package]] @@ -3962,14 +3962,14 @@ test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-mermaid" -version = "2.0.2" +version = "2.1.0" description = "Mermaid diagrams in your Sphinx-powered docs" optional = false python-versions = ">=3.10" groups = ["docs"] files = [ - {file = "sphinxcontrib_mermaid-2.0.2-py3-none-any.whl", hash = "sha256:d862e514991279fb4816302c5cfe167d2557bf3ce7125ae0cb47dac80a0f46ce"}, - {file = "sphinxcontrib_mermaid-2.0.2.tar.gz", hash = "sha256:f09576c78ca93fa0e3034fd9c45aaffa7c44ab449de9c43b8b8d262afe52bc66"}, + {file = "sphinxcontrib_mermaid-2.1.0-py3-none-any.whl", hash = "sha256:417cd144ec4b28852f46ba653f02ce8e538881c812111671a4c30344e87f2112"}, + {file = "sphinxcontrib_mermaid-2.1.0.tar.gz", hash = "sha256:13c5f9ac395cb6abf403eca34e228dc9fb3a30c9d960dbf3e40e9a8cef969549"}, ] [package.dependencies] @@ -4426,4 +4426,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.12,<3.15" -content-hash = "e99f4737f1d271754494ecd15b2bf8014c8075a8a802ea2c2e8f8fcf4d86f091" +content-hash = "ed6ef2a6cb8814ee6ab96c983711908f3102cbfdeb28136410bf675ad2da745c" diff --git a/pyproject.toml b/pyproject.toml index 43294fa45..05ff91165 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,12 +57,12 @@ optional = true [tool.poetry.group.dev.dependencies] jupyter = "^1.1.1" -jupytext = "^1.19.3" +jupytext = "^1.19.5" ipykernel = "^7.3.0" ipympl = "^0.10.0" [tool.poetry.group.test.dependencies] -pytest = "^9.0.3" +pytest = "^9.1.1" pytest-cov = "^7.1.0" ipykernel = "^7.3.0" @@ -71,12 +71,12 @@ optional = true [tool.poetry.group.docs.dependencies] sphinx = "^9.1.0" -sphinx-book-theme = "^1.1.4" +sphinx-book-theme = "^1.4.0" jupyter-sphinx = ">=0.5.3" sphinx-copybutton = ">=0.5.2,<1.0" myst-nb = "^1.4.0" sphinxcontrib-apidoc = ">=0.6.0,<1.0" -sphinxcontrib-mermaid = "^2.0" +sphinxcontrib-mermaid = "^2.1" nbsphinx = "^0.9.8" numpydoc = "^1.10.0" ipykernel = "^7.3.0" From 982f0c7cbc9c853cfd9c2102d31efb13736a2122 Mon Sep 17 00:00:00 2001 From: "astarvienna-bot[bot]" <217837468+myciapp[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 11:59:00 +0000 Subject: [PATCH 67/68] Bumping version from 0.12.0b2.dev4 to 0.12.0b2.dev5 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 05ff91165..da766c6da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ScopeSim" -version = "0.12.0b2.dev4" +version = "0.12.0b2.dev5" description = "Generalised telescope observation simulator" license = {text = "GPL-3.0-or-later"} authors = [ From 719cd68daeb1a745a1226ec38aa84852a52c877a Mon Sep 17 00:00:00 2001 From: Hugo Buddelmeijer Date: Thu, 3 Sep 2026 15:39:38 +0200 Subject: [PATCH 68/68] Mark test_python_image_coords as webtest, because face is downloaded. --- scopesim/tests/tests_optics/test_ImagePlaneUtils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scopesim/tests/tests_optics/test_ImagePlaneUtils.py b/scopesim/tests/tests_optics/test_ImagePlaneUtils.py index 787442e8a..8658dd423 100644 --- a/scopesim/tests/tests_optics/test_ImagePlaneUtils.py +++ b/scopesim/tests/tests_optics/test_ImagePlaneUtils.py @@ -152,6 +152,8 @@ def test_larger_hdu_is_fully_outside_smaller_hdu(self): assert np.sum(new.data) == small_sum + # face is downloaded from the internet. + @pytest.mark.webtest def test_python_image_coords(self): # numpy uses a system of im[y, x] # numpy.shape[0] = y_len, numpy.shape[1] = x_len