From b7f5db677f5bc71e7e822187010ab06a7920f9f2 Mon Sep 17 00:00:00 2001 From: oczoske Date: Tue, 11 Aug 2026 18:12:28 +0200 Subject: [PATCH 1/3] 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 4e60fecb73dc3f56b030ea2f49f03849b317a873 Mon Sep 17 00:00:00 2001 From: oczoske Date: Wed, 12 Aug 2026 16:23:30 +0200 Subject: [PATCH 2/3] 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 3/3] 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):