From 7171eae4637c5303fe163614e894659176704de2 Mon Sep 17 00:00:00 2001 From: Hugo Buddelmeijer Date: Mon, 1 Jun 2026 12:59:23 +0200 Subject: [PATCH 1/4] Round waverange to 8 decimal places to prevent floating point errors --- scopesim/optics/fov.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scopesim/optics/fov.py b/scopesim/optics/fov.py index 503214f5..b92dd1ec 100644 --- a/scopesim/optics/fov.py +++ b/scopesim/optics/fov.py @@ -1165,8 +1165,10 @@ def extract_range_from_spectrum(spectrum, waverange): assert isinstance(spectrum, SourceSpectrum), ( f"spectrum must be of type synphot.SourceSpectrum: {type(spectrum)}") - wave_min, wave_max = quantify(waverange, u.um).to(u.AA).value - spec_waveset = spectrum.waveset.to(u.AA).value + # Round to 8 decimal places, because the conversion to u.AA will cause + # irrelevant floating point differences. + wave_min, wave_max = quantify(waverange, u.um).to(u.AA).value.round(8) + spec_waveset = spectrum.waveset.to(u.AA).value.round(8) mask = (spec_waveset > wave_min) * (spec_waveset < wave_max) # FIXME: Why did I comment this out in 2023? Seems useful to have... From 602e9cd1dce48ba84f6ac4bcacb4e9c45d783f09 Mon Sep 17 00:00:00 2001 From: Hugo Buddelmeijer Date: Mon, 1 Jun 2026 12:59:54 +0200 Subject: [PATCH 2/4] Include endpoint of spectrum in prepare_source --- scopesim/optics/optical_train.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scopesim/optics/optical_train.py b/scopesim/optics/optical_train.py index 4b3448cb..7c5528fe 100644 --- a/scopesim/optics/optical_train.py +++ b/scopesim/optics/optical_train.py @@ -340,7 +340,12 @@ def prepare_source(self, source): wave_max = max(fov.meta["wave_max"] for fov in self.fov_manager.fovs) wave_unit = u.Unit(from_currsys("!SIM.spectral.wave_unit", self.cmds)) dwave = from_currsys("!SIM.spectral.spectral_bin_width", self.cmds) # Not a quantity - fov_waveset = np.arange(wave_min.value, wave_max.value, dwave) * wave_unit + # Include the last point by adding dwave, because arange excludes the last point. + fov_waveset = np.arange(wave_min.value, wave_max.value + dwave, dwave) + # Ensure the start and end values are exact. + fov_waveset[0] = wave_min.value + fov_waveset[-1] = wave_max.value + fov_waveset *= wave_unit fov_waveset = fov_waveset.to(u.um) field.spectra[ispec] = SourceSpectrum(Empirical1D, From f536277201b13058f69c50503af806e8321c7101 Mon Sep 17 00:00:00 2001 From: Hugo Buddelmeijer Date: Mon, 1 Jun 2026 17:40:46 +0200 Subject: [PATCH 3/4] Fix test --- scopesim/tests/tests_optics/test_fov_utls.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scopesim/tests/tests_optics/test_fov_utls.py b/scopesim/tests/tests_optics/test_fov_utls.py index ba08540d..f7252e31 100644 --- a/scopesim/tests/tests_optics/test_fov_utls.py +++ b/scopesim/tests/tests_optics/test_fov_utls.py @@ -112,7 +112,10 @@ def test_extracts_the_wave_range_needed(self): new_spec = extract_range_from_spectrum(spec, waverange) assert len(new_spec.waverange) == 2 - assert new_spec.waverange[0] == 1.98 * u.um + # Cannot test for equality, because this does not hold in astropy.units: + # 1.98 * u.um == 19800.0 * u.AA + # assert new_spec.waverange[0] == 1.98 * u.um + assert abs(new_spec.waverange[0] == 1.98 * u.um) < 1e-10 * u.AA assert new_spec(1.98 * u.um).value == approx(12.8) @pytest.mark.parametrize(("endpoint", "msg"), From 6b8bcbffbe82cd439da3c8a1db00f2d97a816bd1 Mon Sep 17 00:00:00 2001 From: Hugo Buddelmeijer Date: Mon, 1 Jun 2026 20:30:07 +0200 Subject: [PATCH 4/4] Properly recalculate fov_waveset --- scopesim/optics/optical_train.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scopesim/optics/optical_train.py b/scopesim/optics/optical_train.py index 7c5528fe..075ae6af 100644 --- a/scopesim/optics/optical_train.py +++ b/scopesim/optics/optical_train.py @@ -341,7 +341,7 @@ def prepare_source(self, source): wave_unit = u.Unit(from_currsys("!SIM.spectral.wave_unit", self.cmds)) dwave = from_currsys("!SIM.spectral.spectral_bin_width", self.cmds) # Not a quantity # Include the last point by adding dwave, because arange excludes the last point. - fov_waveset = np.arange(wave_min.value, wave_max.value + dwave, dwave) + fov_waveset = np.arange(wave_min.value, wave_max.value + 0.9 * dwave, dwave) # Ensure the start and end values are exact. fov_waveset[0] = wave_min.value fov_waveset[-1] = wave_max.value @@ -387,11 +387,17 @@ def prepare_source(self, source): cube.header["CUNIT2"] = "deg" # Put on fov wavegrid + # TODO: This is the same code as earlier in the function; refactor. wave_min = min(fov.meta["wave_min"] for fov in self.fov_manager.fovs) wave_max = max(fov.meta["wave_max"] for fov in self.fov_manager.fovs) wave_unit = u.Unit(from_currsys("!SIM.spectral.wave_unit", self.cmds)) dwave = from_currsys("!SIM.spectral.spectral_bin_width", self.cmds) # Not a quantity - fov_waveset = np.arange(wave_min.value, wave_max.value, dwave) * wave_unit + # Include the last point by adding dwave, because arange excludes the last point. + fov_waveset = np.arange(wave_min.value, wave_max.value + 0.9 * dwave, dwave) + # Ensure the start and end values are exact. + fov_waveset[0] = wave_min.value + fov_waveset[-1] = wave_max.value + fov_waveset *= wave_unit fov_waveset = fov_waveset.to(u.um) if (wave.to(u.um).min() > fov_waveset.max() or