def _observe_through_filter(bp, B, unit): if not synphot: raise AstropyWarning('synphot is required for bandpass filtering.') obs = Observation(B, bp) wave = obs.effective_wavelength() fluxd = obs.effstim(unit) return wave, fluxd
class CommCase(object): """Base class for commissioning tests.""" obsmode = None # Observation mode string spectrum = None # SYNPHOT-like string to construct spectrum force = None # Default tables are the latest available as of 2016-07-25. tables = { 'graphtable': os.path.join('mtab$OLD_FILES', '07r1502mm_tmg.fits'), 'comptable': os.path.join('mtab$OLD_FILES', '07r1502nm_tmc.fits'), 'thermtable': 'mtab$tae17277m_tmt.fits' } def setup_class(self): """Subclass needs to define ``obsmode`` and ``spectrum`` class variables for this to work. """ if not HAS_PYSYNPHOT: raise ImportError( 'ASTROLIB PYSYNPHOT must be installed to run these tests') # Make sure both software use the same graph and component tables. conf.graphtable = self.tables['graphtable'] conf.comptable = self.tables['comptable'] conf.thermtable = self.tables['thermtable'] S.setref(graphtable=self.tables['graphtable'], comptable=self.tables['comptable'], thermtable=self.tables['thermtable']) # Construct spectra for both software. self.sp = parse_spec(self.spectrum) self.bp = band(self.obsmode) # Astropy version has no prior knowledge of instrument-specific # binset, so it has to be set explicitly. if hasattr(self.bp, 'binset'): self.obs = Observation(self.sp, self.bp, force=self.force, binset=self.bp.binset) else: self.obs = Observation(self.sp, self.bp, force=self.force) # Astropy version does not assume a default waveset # (you either have it or you don't). If there is no # waveset, no point comparing obs waveset against ASTROLIB. if self.sp.waveset is None or self.bp.waveset is None: self._has_obswave = False else: self._has_obswave = True self.spref = old_parse_spec(self.spectrum) self.bpref = S.ObsBandpass(self.obsmode) self.obsref = S.Observation(self.spref, self.bpref, force=self.force) # Ensure we are comparing in the same units self.bpref.convert(self.bp._internal_wave_unit.name) self.spref.convert(self.sp._internal_wave_unit.name) self.spref.convert(self.sp._internal_flux_unit.name) self.obsref.convert(self.obs._internal_wave_unit.name) self.obsref.convert(self.obs._internal_flux_unit.name) @staticmethod def _get_new_wave(sp): """Astropy version does not assume a default waveset (you either have it or you don't). This is a convenience method to duck-type ASTROLIB waveset behavior. """ wave = sp.waveset if wave is None: wave = conf.waveset_array else: wave = wave.value return wave def _assert_allclose(self, actual, desired, rtol=1e-07, atol=sys.float_info.min): """``assert_allclose`` only report percentage but we also want to know some extra info conveniently.""" if isinstance(actual, Iterable): ntot = len(actual) else: ntot = 1 n = np.count_nonzero( abs(actual - desired) > atol + rtol * abs(desired)) msg = 'obsmode: {0}\nspectrum: {1}\n(mismatch {2}/{3})'.format( self.obsmode, self.spectrum, n, ntot) assert_allclose(actual, desired, rtol=rtol, atol=atol, err_msg=msg) # TODO: Confirm whether non-default atol is acceptable. # Have to use this value to avoid AssertionError for very # small non-zero flux values like 1.8e-26 to 2e-311. def _compare_nonzero(self, new, old, thresh=0.01, atol=1e-29): """Compare normally when results from both are non-zero.""" i = (new != 0) & (old != 0) # Make sure non-zero atol is not too high, otherwise just let it fail. if atol > (thresh * min(new.max(), old.max())): atol = sys.float_info.min self._assert_allclose(new[i], old[i], rtol=thresh, atol=atol) def _compare_zero(self, new, old, thresh=0.01): """Special handling for comparison when one of the results is zero. This is because ``rtol`` will not work.""" i = ((new == 0) | (old == 0)) & (new != old) try: self._assert_allclose(new[i], old[i], rtol=thresh) except AssertionError as e: pytest.xfail(str(e)) # TODO: Will revisit later def test_band_wave(self, thresh=0.01): """Test bandpass waveset.""" wave = self._get_new_wave(self.bp) self._assert_allclose(wave, self.bpref.wave, rtol=thresh) def test_spec_wave(self, thresh=0.01): """Test source spectrum waveset.""" wave = self._get_new_wave(self.sp) # TODO: Failure due to different wavesets for blackbody; Ignore? try: self._assert_allclose(wave, self.spref.wave, rtol=thresh) except (AssertionError, ValueError) as e: self._has_obswave = False # Skip obs waveset tests if 'bb(' in self.spectrum: pytest.xfail('Blackbody waveset implementations are different') elif 'unit(' in self.spectrum: pytest.xfail('Flat does not use default waveset anymore') else: raise def test_obs_wave(self, thresh=0.01): """Test observation waveset.""" if not self._has_obswave: # Nothing to test return # Native wave = self.obs.waveset.value # TODO: Failure due to different wavesets for blackbody; Ignore? try: self._assert_allclose(wave, self.obsref.wave, rtol=thresh) except (AssertionError, ValueError) as e: if 'bb(' in self.spectrum: pytest.xfail('Blackbody waveset implementations are different') elif 'unit(' in self.spectrum: self._has_obswave = False # Skip binned flux test pytest.xfail('Flat does not use default waveset anymore') else: raise # Binned binset = self.obs.binset.value self._assert_allclose(binset, self.obsref.binwave, rtol=thresh) @pytest.mark.parametrize('thrutype', ['zero', 'nonzero']) def test_band_thru(self, thrutype, thresh=0.01): """Test bandpass throughput, which is always between 0 and 1.""" wave = self.bpref.wave thru = self.bp(wave).value if thrutype == 'zero': self._compare_zero(thru, self.bpref.throughput, thresh=thresh) else: # nonzero self._compare_nonzero(thru, self.bpref.throughput, thresh=thresh) @pytest.mark.parametrize('fluxtype', ['zero', 'nonzero']) def test_spec_flux(self, fluxtype, thresh=0.01): """Test flux for source spectrum in PHOTLAM.""" wave = self.spref.wave flux = self.sp(wave).value if fluxtype == 'zero': self._compare_zero(flux, self.spref.flux, thresh=thresh) else: # nonzero self._compare_nonzero(flux, self.spref.flux, thresh=thresh) @pytest.mark.parametrize('fluxtype', ['zero', 'nonzero']) def test_obs_flux(self, fluxtype, thresh=0.01): """Test flux for observation in PHOTLAM.""" wave = self.obsref.wave flux = self.obs(wave).value # Native if fluxtype == 'zero': self._compare_zero(flux, self.obsref.flux, thresh=thresh) else: # nonzero self._compare_nonzero(flux, self.obsref.flux, thresh=thresh) if not self._has_obswave: # Do not compare binned flux return # Binned (cannot be resampled) binflux = self.obs.binflux.value if fluxtype == 'zero': self._compare_zero(binflux, self.obsref.binflux, thresh=thresh) else: # nonzero try: self._compare_nonzero(binflux, self.obsref.binflux, thresh=thresh) except AssertionError as e: if 'unit(' in self.spectrum: pytest.xfail('Flat does not use default waveset anymore:\n' '{0}'.format(str(e))) else: raise def test_countrate(self, thresh=0.01): """Test observation countrate calculations.""" ans = self.obsref.countrate() # Astropy version does not assume a default area. val = self.obs.countrate(conf.area).value self._assert_allclose(val, ans, rtol=thresh) def test_efflam(self, thresh=0.01): """Test observation effective wavelength.""" ans = self.obsref.efflam() val = self.obs.effective_wavelength().value self._assert_allclose(val, ans, rtol=thresh) def teardown_class(self): """Reset config for both software.""" for cfgname in self.tables: conf.reset(cfgname) S.setref()
class CommCase(object): """Base class for commissioning tests.""" obsmode = None # Observation mode string spectrum = None # SYNPHOT-like string to construct spectrum force = None # Default tables are the latest available as of 2016-07-25. tables = { 'graphtable': os.path.join('mtab$OLD_FILES', '07r1502mm_tmg.fits'), 'comptable': os.path.join('mtab$OLD_FILES', '07r1502nm_tmc.fits'), 'thermtable': 'mtab$tae17277m_tmt.fits'} def setup_class(self): """Subclass needs to define ``obsmode`` and ``spectrum`` class variables for this to work. """ if not HAS_PYSYNPHOT: raise ImportError( 'ASTROLIB PYSYNPHOT must be installed to run these tests') # Make sure both software use the same graph and component tables. conf.graphtable = self.tables['graphtable'] conf.comptable = self.tables['comptable'] conf.thermtable = self.tables['thermtable'] S.setref(graphtable=self.tables['graphtable'], comptable=self.tables['comptable'], thermtable=self.tables['thermtable']) # Construct spectra for both software. self.sp = parse_spec(self.spectrum) self.bp = band(self.obsmode) # Astropy version has no prior knowledge of instrument-specific # binset, so it has to be set explicitly. if hasattr(self.bp, 'binset'): self.obs = Observation(self.sp, self.bp, force=self.force, binset=self.bp.binset) else: self.obs = Observation(self.sp, self.bp, force=self.force) # Astropy version does not assume a default waveset # (you either have it or you don't). If there is no # waveset, no point comparing obs waveset against ASTROLIB. if self.sp.waveset is None or self.bp.waveset is None: self._has_obswave = False else: self._has_obswave = True self.spref = old_parse_spec(self.spectrum) self.bpref = S.ObsBandpass(self.obsmode) self.obsref = S.Observation(self.spref, self.bpref, force=self.force) # Ensure we are comparing in the same units self.bpref.convert(self.bp._internal_wave_unit.name) self.spref.convert(self.sp._internal_wave_unit.name) self.spref.convert(self.sp._internal_flux_unit.name) self.obsref.convert(self.obs._internal_wave_unit.name) self.obsref.convert(self.obs._internal_flux_unit.name) @staticmethod def _get_new_wave(sp): """Astropy version does not assume a default waveset (you either have it or you don't). This is a convenience method to duck-type ASTROLIB waveset behavior. """ wave = sp.waveset if wave is None: wave = conf.waveset_array else: wave = wave.value return wave def _assert_allclose(self, actual, desired, rtol=1e-07, atol=sys.float_info.min): """``assert_allclose`` only report percentage but we also want to know some extra info conveniently.""" if isinstance(actual, Iterable): ntot = len(actual) else: ntot = 1 n = np.count_nonzero( abs(actual - desired) > atol + rtol * abs(desired)) msg = 'obsmode: {0}\nspectrum: {1}\n(mismatch {2}/{3})'.format( self.obsmode, self.spectrum, n, ntot) assert_allclose(actual, desired, rtol=rtol, atol=atol, err_msg=msg) # TODO: Confirm whether non-default atol is acceptable. # Have to use this value to avoid AssertionError for very # small non-zero flux values like 1.8e-26 to 2e-311. def _compare_nonzero(self, new, old, thresh=0.01, atol=1e-29): """Compare normally when results from both are non-zero.""" i = (new != 0) & (old != 0) # Make sure non-zero atol is not too high, otherwise just let it fail. if atol > (thresh * min(new.max(), old.max())): atol = sys.float_info.min self._assert_allclose(new[i], old[i], rtol=thresh, atol=atol) def _compare_zero(self, new, old, thresh=0.01): """Special handling for comparison when one of the results is zero. This is because ``rtol`` will not work.""" i = ((new == 0) | (old == 0)) & (new != old) try: self._assert_allclose(new[i], old[i], rtol=thresh) except AssertionError as e: pytest.xfail(str(e)) # TODO: Will revisit later def test_band_wave(self, thresh=0.01): """Test bandpass waveset.""" wave = self._get_new_wave(self.bp) self._assert_allclose(wave, self.bpref.wave, rtol=thresh) def test_spec_wave(self, thresh=0.01): """Test source spectrum waveset.""" wave = self._get_new_wave(self.sp) # TODO: Failure due to different wavesets for blackbody; Ignore? try: self._assert_allclose(wave, self.spref.wave, rtol=thresh) except (AssertionError, ValueError): self._has_obswave = False # Skip obs waveset tests if 'bb(' in self.spectrum: pytest.xfail('Blackbody waveset implementations are different') elif 'unit(' in self.spectrum: pytest.xfail('Flat does not use default waveset anymore') else: raise def test_obs_wave(self, thresh=0.01): """Test observation waveset.""" if not self._has_obswave: # Nothing to test return # Native wave = self.obs.waveset.value # TODO: Failure due to different wavesets for blackbody; Ignore? try: self._assert_allclose(wave, self.obsref.wave, rtol=thresh) except (AssertionError, ValueError): if 'bb(' in self.spectrum: pytest.xfail('Blackbody waveset implementations are different') elif 'unit(' in self.spectrum: self._has_obswave = False # Skip binned flux test pytest.xfail('Flat does not use default waveset anymore') else: raise # Binned binset = self.obs.binset.value self._assert_allclose(binset, self.obsref.binwave, rtol=thresh) @pytest.mark.parametrize('thrutype', ['zero', 'nonzero']) def test_band_thru(self, thrutype, thresh=0.01): """Test bandpass throughput, which is always between 0 and 1.""" wave = self.bpref.wave thru = self.bp(wave).value if thrutype == 'zero': self._compare_zero(thru, self.bpref.throughput, thresh=thresh) else: # nonzero self._compare_nonzero(thru, self.bpref.throughput, thresh=thresh) @pytest.mark.parametrize('fluxtype', ['zero', 'nonzero']) def test_spec_flux(self, fluxtype, thresh=0.01): """Test flux for source spectrum in PHOTLAM.""" wave = self.spref.wave flux = self.sp(wave).value if fluxtype == 'zero': self._compare_zero(flux, self.spref.flux, thresh=thresh) else: # nonzero self._compare_nonzero(flux, self.spref.flux, thresh=thresh) @pytest.mark.parametrize('fluxtype', ['zero', 'nonzero']) def test_obs_flux(self, fluxtype, thresh=0.01): """Test flux for observation in PHOTLAM.""" wave = self.obsref.wave flux = self.obs(wave).value # Native if fluxtype == 'zero': self._compare_zero(flux, self.obsref.flux, thresh=thresh) else: # nonzero self._compare_nonzero(flux, self.obsref.flux, thresh=thresh) if not self._has_obswave: # Do not compare binned flux return # Binned (cannot be resampled) binflux = self.obs.binflux.value if fluxtype == 'zero': self._compare_zero(binflux, self.obsref.binflux, thresh=thresh) else: # nonzero try: self._compare_nonzero(binflux, self.obsref.binflux, thresh=thresh) except AssertionError as e: if 'unit(' in self.spectrum: pytest.xfail('Flat does not use default waveset anymore:\n' '{0}'.format(str(e))) else: raise def test_countrate(self, thresh=0.01): """Test observation countrate calculations.""" ans = self.obsref.countrate() # Astropy version does not assume a default area. val = self.obs.countrate(conf.area).value self._assert_allclose(val, ans, rtol=thresh) def test_efflam(self, thresh=0.01): """Test observation effective wavelength.""" ans = self.obsref.efflam() val = self.obs.effective_wavelength().value self._assert_allclose(val, ans, rtol=thresh) def teardown_class(self): """Reset config for both software.""" for cfgname in self.tables: conf.reset(cfgname) S.setref()