def setup_two(make_data_path): from sherpa.astro.io import read_pha, read_arf, read_rmf from sherpa.astro import xspec abs1 = xspec.XSphabs('abs1') p1 = PowLaw1D('p1') model = abs1 * p1 * 1e-4 pi2278 = make_data_path("pi2278.fits") pi2286 = make_data_path("pi2286.fits") data_pi2278 = read_pha(pi2278) data_pi2286 = read_pha(pi2286) data_pi2278.set_rmf(read_rmf(make_data_path('rmf2278.fits'))) data_pi2278.set_arf(read_arf(make_data_path('arf2278.fits'))) data_pi2286.set_rmf(read_rmf(make_data_path('rmf2286.fits'))) data_pi2286.set_arf(read_arf(make_data_path('arf2286.fits'))) rsp_pi2278 = Response1D(data_pi2278) rsp_pi2286 = Response1D(data_pi2286) return { 'data_pi2278': data_pi2278, 'data_pi2286': data_pi2286, 'model_pi2278': rsp_pi2278(model), 'model_pi2286': rsp_pi2286(model) }
def setUp(self): self._old_logger_level = logger.getEffectiveLevel() logger.setLevel(logging.ERROR) from sherpa.astro.io import read_pha from sherpa.astro import xspec # Ensure we have a known set of XSPEC settings. # At present this is just the abundance and cross-section, # since the cosmology settings do not affect any of the # models used here. # self._xspec_settings = { 'abund': xspec.get_xsabund(), 'xsect': xspec.get_xsxsect() } xspec.set_xsabund('angr') xspec.set_xsxsect('bcmc') pha_fname = self.make_path("9774.pi") self.data = read_pha(pha_fname) self.data.notice(0.5, 7.0) bkg_fname = self.make_path("9774_bg.pi") self.bkg = read_pha(bkg_fname) abs1 = xspec.XSphabs('abs1') p1 = PowLaw1D('p1') self.model = abs1 + p1 self.model_mult = abs1 * p1 pi2278 = self.make_path("pi2278.fits") pi2286 = self.make_path("pi2286.fits") self.data_pi2278 = read_pha(pi2278) self.data_pi2286 = read_pha(pi2286)
def setUp(self): try: from sherpa.astro.io import read_pha from sherpa.astro.xspec import XSwabs, XSpowerlaw except: return # self.startdir = os.getcwd() self.old_level = logger.getEffectiveLevel() logger.setLevel(logging.CRITICAL) pha = self.make_path("refake_0934_1_21_1e4.fak") # rmf = self.make_path("ccdid7_default.rmf") # arf = self.make_path("quiet_0934.arf") self.simarf = self.make_path("aref_sample.fits") self.pcaarf = self.make_path("aref_Cedge.fits") data = read_pha(pha) data.ignore(None, 0.3) data.ignore(7.0, None) rsp = Response1D(data) self.abs1 = XSwabs('abs1') self.p1 = XSpowerlaw('p1') model = rsp(self.abs1 * self.p1) self.fit = Fit(data, model, CStat(), NelderMead(), Covariance())
def loadup_3c273(use_errors, subt, noticed, make_data_path): """Load up the 3C273 data. Requires @requires_fits and @requires_data on the test. """ # We could create a PHA object but it's easiest to use # an on-disk version to setup everything. # path = make_data_path("3c273.pi") pha = io.read_pha(path, use_errors=use_errors) if subt: pha.subtract() else: pha.unsubtract() if noticed: # After this, we have both # pha.get_filter(format="%.3f") # pha.get_background().get_filter(format="%.3f") # returning # "0.518:1.986,4.869:8.220" # # In channels this is # "36:136,334:563" # pha.notice(0.5, 2) pha.notice(5, 7) return pha
def test_dataphahistogram_prepare_wavelength(make_data_path): """Check we can use wavelength setting""" from sherpa.astro.io import read_pha # could fake a dataset but it's easier to use one infile = make_data_path('3c273.pi') pha = read_pha(infile) pha.name = 'my-name.pi' # Also check out the type='counts' option pha.set_analysis('wave', type='counts') pha.notice(3, 5) plot = aplot.DataPHAPlot() plot.prepare(pha) assert plot.xlabel == 'Wavelength (Angstrom)' assert plot.ylabel == 'Counts/Angstrom' assert plot.title == 'my-name.pi' # data is inverted assert plot.xlo[0] > plot.xlo[-1] # can we access the "pseudo" x attribute? assert plot.x[0] > plot.x[-1] assert np.all(plot.y > 0) # regression test yexp = np.asarray([39.44132393, 68.92072985, 64.39425057, 48.69954162, 41.17454851, 88.87982014, 74.70504415, 79.22094498, 94.57773635]) assert plot.y == pytest.approx(yexp)
def setUp(self): self._old_logger_level = logger.getEffectiveLevel() logger.setLevel(logging.ERROR) from sherpa.astro.xspec import XSphabs from sherpa.astro.io import read_pha pha_fname = self.make_path("9774.pi") self.data = read_pha(pha_fname) self.data.notice(0.5, 7.0) bkg_fname = self.make_path("9774_bg.pi") self.bkg = read_pha(bkg_fname) abs1 = XSphabs('abs1') p1 = PowLaw1D('p1') self.model = abs1 + p1
def setup(make_data_path): from sherpa.astro.io import read_pha from sherpa.astro.xspec import XSwabs, XSpowerlaw old_level = logger.getEffectiveLevel() logger.setLevel(logging.CRITICAL) pha = make_data_path("refake_0934_1_21_1e4.fak") simarf = make_data_path("aref_sample.fits") pcaarf = make_data_path("aref_Cedge.fits") data = read_pha(pha) data.ignore(None, 0.3) data.ignore(7.0, None) rsp = Response1D(data) abs1 = XSwabs('abs1') p1 = XSpowerlaw('p1') model = rsp(abs1 * p1) abs1.nh = 0.092886 p1.phoindex = 0.994544 p1.norm = 9.26369 fit = Fit(data, model, CStat(), NelderMead(), Covariance()) yield {'simarf': simarf, 'pcaarf': pcaarf, 'niter': 10, 'fit': fit} # Reset the logger logger.setLevel(old_level)
def test_sourceplot_prepare_wavelength(make_data_path): """Check we can use wavelength setting""" from sherpa.astro.io import read_pha # could fake a dataset but it's easier to use one infile = make_data_path('3c273.pi') pha = read_pha(infile) pha.name = 'my-name.pi' pha.set_analysis('wave') pha.notice(3, 5) mdl = Const1D() # not bothered too much about the model (e.g. setting a response) # plot = aplot.SourcePlot() plot.prepare(pha, mdl) assert plot.xlabel == 'Wavelength (Angstrom)' assert plot.ylabel == 'f(lambda) Photons/sec/cm$^2$/Angstrom ' assert plot.title == 'Source Model of my-name.pi' # data is inverted assert plot.xlo[0] > plot.xlo[-1] assert plot.xlo[0] > plot.xhi[0] assert np.all(plot.y > 0) assert plot.y.size == 1090
def setUp(self): try: from sherpa.astro.io import read_pha from sherpa.astro.xspec import XSwabs, XSpowerlaw except: return #self.startdir = os.getcwd() self.old_level = logger.getEffectiveLevel() logger.setLevel(logging.CRITICAL) datadir = SherpaTestCase.datadir if datadir is None: return pha = os.path.join(datadir, "refake_0934_1_21_1e4.fak") rmf = os.path.join(datadir, "ccdid7_default.rmf") arf = os.path.join(datadir, "quiet_0934.arf") self.simarf = os.path.join(datadir, "aref_sample.fits") self.pcaarf = os.path.join(datadir, "aref_Cedge.fits") data = read_pha(pha) data.ignore(None,0.3) data.ignore(7.0,None) rsp = Response1D(data) self.abs1 = XSwabs('abs1') self.p1 = XSpowerlaw('p1') model = rsp(self.abs1*self.p1) self.fit = Fit(data, model, CStat(), NelderMead(), Covariance())
def test_bug38_filtering_grouping(make_data_path): """Low-level tests related to bugs #38, #917: filter+group""" from sherpa.astro.io import read_pha pha = read_pha(make_data_path('3c273.pi')) pha.notice(1, 6) pha.ignore(3, 4) assert pha.get_filter(group=True, format='%.4f') == '1.0147:2.7886,4.1391:6.2342' assert pha.get_filter(group=False, format='%.4f') == '1.0001:2.8543,4.0369:6.5627' pha.group_width(40) assert pha.get_filter(group=True, format='%.4f') == '0.8760:2.6280,3.7960:6.7160' assert pha.get_filter(group=False, format='%.4f') == '0.5913:2.9127,3.5113:7.0007' assert pha.mask.size == 26 assert pha.mask.sum() == 10 assert pha.mask[1:5].all() assert pha.mask[6:12].all() # get the ungrouped mask mask = pha.get_mask() assert mask.sum() == 10 * 40 assert mask[40:200].all() assert mask[240:480].all() # check filtered bins elo_all, ehi_all = pha._get_ebins(group=False) elo, ehi = pha._get_ebins(group=True) assert elo[1] == elo_all[40] assert ehi[11] == ehi_all[479]
def setUp(self): self._old_logger_level = logger.getEffectiveLevel() logger.setLevel(logging.ERROR) from sherpa.astro.xspec import XSphabs from sherpa.astro.io import read_pha pha_fname = self.make_path("9774.pi") self.data = read_pha(pha_fname) self.data.notice(0.5, 7.0) bkg_fname = self.make_path("9774_bg.pi") self.bkg = read_pha(bkg_fname) abs1 = XSphabs("abs1") p1 = PowLaw1D("p1") self.model = abs1 + p1
def setup_bkg(make_data_path): from sherpa.astro.io import read_pha, read_arf, read_rmf from sherpa.astro import xspec infile = make_data_path("9774_bg.pi") bkg = read_pha(infile) bkg.exposure = 1 arf = read_arf(make_data_path('9774.arf')) rmf = read_rmf(make_data_path('9774.rmf')) bkg.set_arf(arf) bkg.set_rmf(rmf) bkg.set_analysis('energy') bkg.notice(0.5, 7.0) # We stay with a linear scale for the absorption model # here as the values don't seem to go below 0.1. # abs1 = xspec.XSwabs('abs1') p1 = PowLaw1D('p1') model = abs1 * p1 p1.ampl = 1e-4 rsp = Response1D(bkg) return {'bkg': bkg, 'model': rsp(model)}
def test_pha_real(subtract, group, make_data_path, override_plot_backend): """This is grouped and has a background""" from sherpa.astro.io import read_pha d = read_pha(make_data_path('3c273.pi')) d.name = '3c273.pi' if not group: d.ungroup() d.notice(0.5, 7) if subtract: d.subtract() r = d._repr_html_() check(r, 'PHA', '3c273.pi', 'COUNTS', nmeta=11) assert '<div class="dataval">2000-01-10T06:47:15</div>' in r assert '<div class="dataname">Background</div>' in r assert '<div class="dataname">Grouping</div>' in r if subtract: assert '<div class="dataval">Subtracted</div>' in r else: assert '<div class="dataval">Not subtracted</div>' in r if group: assert '<div class="dataval">Applied (46 groups)</div>' in r assert '<div class="dataval">0.4672-9.8696 Energy (keV) with 42 groups</div>' in r else: assert '<div class="dataval">Not applied</div>' in r assert '<div class="dataval">0.4964-7.0080 Energy (keV) with 446 channels</div>' in r
def test_read_pha_fails_rmf(make_data_path): """Just check in we can't read in a RMF as a PHA file.""" if backend_is("pyfits"): emsg = " does not appear to be a PHA spectrum" etype = IOErr elif backend_is("crates"): emsg = "dmKeyRead() could not find key. 'HDUCLAS1'" etype = ValueError else: assert False, f"Internal error: unknown backend {io.backend}" infile = make_data_path(RMFFILE) with pytest.raises(etype) as excinfo: io.read_pha(infile) assert emsg in str(excinfo.value)
def test_read_pha_fails_arf(make_data_path): """Just check in we can't read in an ARF as a PHA file.""" if backend_is("pyfits"): emsg = " does not appear to be a PHA spectrum" etype = IOErr elif backend_is("crates"): emsg = "File must be a PHA file." etype = TypeError else: assert False, f"Internal error: unknown backend {io.backend}" infile = make_data_path(ARFFILE) with pytest.raises(etype) as excinfo: io.read_pha(infile) assert emsg in str(excinfo.value)
def test_read_pha_fails_rmf(make_data_path): """Just check in we can't read in a RMF as a PHA file.""" if backend == 'pyfits': emsg = " does not appear to be a PHA spectrum" etype = IOErr elif backend == 'crates': emsg = "dmKeyRead() could not find key. 'HDUCLAS1'" etype = ValueError else: assert False, "Internal error: unknown backend {}".format(backend) infile = make_data_path(RMFFILE) with pytest.raises(etype) as excinfo: io.read_pha(infile) assert emsg in str(excinfo.value)
def test_read_pha_fails_arf(make_data_path): """Just check in we can't read in an ARF as a PHA file.""" if backend == 'pyfits': emsg = " does not appear to be a PHA spectrum" etype = IOErr elif backend == 'crates': emsg = 'File must be a PHA file.' etype = TypeError else: assert False, "Internal error: unknown backend {}".format(backend) infile = make_data_path(ARFFILE) with pytest.raises(etype) as excinfo: io.read_pha(infile) assert emsg in str(excinfo.value)
def test_read_pha_fails_rmf(make_data_path): """Just check in we can't read in a RMF as a PHA file.""" if backend == 'pyfits': emsg = " does not appear to be a PHA spectrum" etype = IOErr elif backend == 'crates': emsg = 'File must be a PHA file.' etype = TypeError else: assert False, "Internal error: unknown backend {}".format(backend) infile = make_data_path(RMFFILE) with pytest.raises(etype) as excinfo: io.read_pha(infile) assert emsg in str(excinfo.value)
def setUp(self): try: from sherpa.astro.xspec import XSphabs, XSpowerlaw from sherpa.astro.io import read_pha except: return pha_fname = self.make_path("stats/9774.pi") self.data = read_pha(pha_fname) self.data.notice(0.5, 7.0) bkg_fname = self.make_path("stats/9774_bg.pi") self.bkg = read_pha(bkg_fname) abs1 = XSphabs('abs1') p1 = PowLaw1D('p1') self.model = abs1 + p1
def test_bug38_filtering(make_data_path): """Low-level tests related to bugs #38, #917: filter""" from sherpa.astro.io import read_pha pha = read_pha(make_data_path('3c273.pi')) assert pha.mask is True pha.notice(0.3, 2) assert pha.mask.size == 46 assert pha.mask.sum() == 25 assert pha.mask[1:26].all()
def setUp(self): self._old_logger_level = logger.getEffectiveLevel() logger.setLevel(logging.ERROR) from sherpa.astro.io import read_pha from sherpa.astro.xspec import XSphabs pha_fname = self.make_path("9774.pi") self.data = read_pha(pha_fname) self.data.notice(0.5, 7.0) bkg_fname = self.make_path("9774_bg.pi") self.bkg = read_pha(bkg_fname) abs1 = XSphabs('abs1') p1 = PowLaw1D('p1') self.model = abs1 + p1 self.model_mult = abs1 * p1 pi2278 = self.make_path("pi2278.fits") pi2286 = self.make_path("pi2286.fits") self.data_pi2278 = read_pha(pi2278) self.data_pi2286 = read_pha(pi2286)
def test_xmmrgs_notice(make_data_path): '''Test that notice and ignore works on XMMRGS dataset, which is ordered in increasing wavelength, not energy''' from sherpa.astro.io import read_pha, read_rmf dat = read_pha(make_data_path('xmmrgs/P0112880201R1S004SRSPEC1003.FTZ')) rmf = read_rmf(make_data_path('xmmrgs/P0112880201R1S004RSPMAT1003.FTZ')) dat.set_rmf(rmf) dat.units = 'wave' dat.notice(18.8, 19.2) assert len(dat.get_dep(filter=True)) == 41 assert dat.get_filter(format='%.2f') == '18.80:19.21' dat.ignore(10, 19.) assert len(dat.get_dep(filter=True)) == 20 assert dat.get_filter(format='%.2f') == '19.01:19.21'
def test_read_pha(make_data_path): """Can we read in a Swift PHA file.""" infile = make_data_path(PHAFILE) pha = io.read_pha(infile) assert isinstance(pha, DataPHA) # assert_allclose defaults to a tolerance of 1e-7 # which is close to the precision these values are stored # in the file. assert_allclose(pha.exposure, 4812.26699895) assert_allclose(pha.backscal, 1.148e-3) assert_allclose(pha.areascal, 1.0) # Although channel is an integer, it's treated as a # float. Note that the channels start at 0 in the file, # but they are promoted to 1 by the crates backend. # XSPEC 12.9.1b, on reading this file, reports channel numbers # between 1 and 1024 inclusive. # nchan = 1024 assert_allclose(pha.channel, np.arange(1, nchan + 1)) # Rather than check each element, use some simple summary # statistics. assert len(pha.counts) == nchan assert_allclose(pha.counts.min(), 0.0) assert_allclose(pha.counts.max(), 3.0) assert_allclose(pha.counts.sum(), 58.0) assert np.argmax(pha.counts) == 110 for field in [ 'staterror', 'syserror', 'bin_lo', 'bin_hi', 'grouping', 'quality' ]: assert getattr(pha, field) is None assert pha.grouped is False assert pha.subtracted is False assert pha.units == 'channel' assert pha.rate assert pha.plot_fac == 0 assert pha.response_ids == [] assert pha.background_ids == [] assert pha.get_background() is None
def test_write_pha_fits_basic_roundtrip(tmp_path): """A very-basic PHA output No ancillary information and no header. """ chans = np.arange(1, 5, dtype=np.int16) counts = np.asarray([1, 0, 3, 2], dtype=np.int16) pha = DataPHA("testy", chans, counts) outfile = tmp_path / "out.pi" io.write_pha(str(outfile), pha, ascii=False, clobber=False) pha = None inpha = io.read_pha(str(outfile)) assert isinstance(inpha, DataPHA) assert inpha.channel == pytest.approx(chans) assert inpha.counts == pytest.approx(counts) for field in ["staterror", "syserror", "bin_lo", "bin_hi", "grouping", "quality", "exposure"]: assert getattr(inpha, field) is None assert inpha.backscal == pytest.approx(1.0) assert inpha.areascal == pytest.approx(1.0) assert not inpha.grouped assert not inpha.subtracted assert inpha.units == "channel" assert inpha.rate assert inpha.plot_fac == 0 assert inpha.response_ids == [] assert inpha.background_ids == [] if backend_is("crates"): check_write_pha_fits_basic_roundtrip_crates(outfile) elif backend_is("pyfits"): check_write_pha_fits_basic_roundtrip_pyfits(outfile) else: # Technically this could be dummy_backend but it would have # failed earlier. # raise RuntimeError(f"Unknown io backend: {io.backend}")
def test_read_pha(make_data_path): """Can we read in a Swift PHA file.""" infile = make_data_path(PHAFILE) pha = io.read_pha(infile) assert isinstance(pha, DataPHA) # assert_allclose defaults to a tolerance of 1e-7 # which is close to the precision these values are stored # in the file. assert_allclose(pha.exposure, 4812.26699895) assert_allclose(pha.backscal, 1.148e-3) assert_allclose(pha.areascal, 1.0) # Although channel is an integer, it's treated as a # float. Note that the channels start at 0 in the file, # but they are promoted to 1 by the crates backend. # XSPEC 12.9.1b, on reading this file, reports channel numbers # between 1 and 1024 inclusive. # nchan = 1024 assert_allclose(pha.channel, np.arange(1, nchan + 1)) # Rather than check each element, use some simple summary # statistics. assert len(pha.counts) == nchan assert_allclose(pha.counts.min(), 0.0) assert_allclose(pha.counts.max(), 3.0) assert_allclose(pha.counts.sum(), 58.0) assert np.argmax(pha.counts) == 110 for field in ['staterror', 'syserror', 'bin_lo', 'bin_hi', 'grouping', 'quality']: assert getattr(pha, field) is None assert pha.grouped is False assert pha.subtracted is False assert pha.units == 'channel' assert pha.rate assert pha.plot_fac == 0 assert pha.response_ids == [] assert pha.background_ids == [] assert pha.get_background() is None
def setup(make_data_path): from sherpa.astro.io import read_pha from sherpa.astro import xspec infile = make_data_path("9774.pi") data = read_pha(infile) data.notice(0.3, 7.0) # Change the exposure time to make the fitted amplitude # > 1 # data.exposure = 1 # Use the wabs model because it is unlikely to change # (as scientifically it is no-longer useful). The problem # with using something like the phabs model is that # changes to that model in XSPEC could change the results # here. # # We fit the log of the nH since this makes the numbers # a bit closer to O(1), and so checking should be easier. # abs1 = xspec.XSwabs('abs1') p1 = PowLaw1D('p1') factor = Const1D('factor') factor.integrate = False model = abs1 * p1 + 0 * factor factor.c0 = 0 abs1.nh = 10**factor.c0 # Ensure the nh limits are honoured by factor (upper limit only). # If you don't do this then the fit can fail because a value # outside the abs1.nh but within factor.c0 can be picked. # factor.c0.max = numpy.log10(abs1.nh.max) rsp = Response1D(data) return {'data': data, 'model': rsp(model)}
def test_1209_background(make_data_path): """Do we pick up the header keywords from the background? This is related to issue #1209 """ # We could set up channels and counts, but let's not. # d = DataPHA("dummy", None, None) assert d.header["TELESCOP"] == "none" assert d.header["INSTRUME"] == "none" assert d.header["FILTER"] == "none" infile = make_data_path(PHAFILE) bkg = io.read_pha(infile) d.set_background(bkg) # The PHA file contains a FILTER keyword but the responses do not. # assert d.header["TELESCOP"] == "SWIFT" assert d.header["INSTRUME"] == "XRT" assert d.header["FILTER"] == "NONE"
def test_astro_data_plot_with_stat_simple(make_data_path, stat): from sherpa.astro import io infile = make_data_path('3c273.pi') pha = io.read_pha(infile) # tweak the data set so that we aren't using the default # options (it shouldn't matter for this test but just # in case). # # Note that background subtraction would normally be an issue # for some of the stats (e.g. WStat), but this shouldn't # trigger a problem here. # pha.set_analysis('energy') pha.subtract() pha.ignore(None, 0.5) pha.ignore(7.0, None) dplot = DataPHAPlot() dplot.prepare(pha, stat=stat)
def test_bug38_filtering_grouping(make_data_path): """Low-level tests related to bugs #38, #917: filter+group""" from sherpa.astro.io import read_pha pha = read_pha(make_data_path('3c273.pi')) pha.notice(1, 6) pha.ignore(3, 4) expected = '0.9928:2.8616,4.0296:6.5700' assert pha.get_filter(group=True, format='%.4f') == expected assert pha.get_filter(group=False, format='%.4f') == expected pha.group_width(40) expected = '0.5840:2.9200,3.5040:7.0080' assert pha.get_filter(group=True, format='%.4f') == expected assert pha.get_filter(group=False, format='%.4f') == expected assert pha.mask.size == 26 assert pha.mask.sum() == 10 assert pha.mask[1:5].all() assert pha.mask[6:12].all() # get the ungrouped mask mask = pha.get_mask() assert mask.sum() == 10 * 40 assert mask[40:200].all() assert mask[240:480].all() # check filtered bins elo_all, ehi_all = pha._get_ebins(group=False) elo, ehi = pha._get_ebins(group=True) assert elo[1] == elo_all[40] assert ehi[11] == ehi_all[479]
def setUp(self): from sherpa.astro.io import read_pha from sherpa.astro.xspec import XSwabs, XSpowerlaw # self.startdir = os.getcwd() self.old_level = logger.getEffectiveLevel() logger.setLevel(logging.CRITICAL) pha = self.make_path("refake_0934_1_21_1e4.fak") # rmf = self.make_path("ccdid7_default.rmf") # arf = self.make_path("quiet_0934.arf") self.simarf = self.make_path("aref_sample.fits") self.pcaarf = self.make_path("aref_Cedge.fits") data = read_pha(pha) data.ignore(None, 0.3) data.ignore(7.0, None) rsp = Response1D(data) self.abs1 = XSwabs('abs1') self.p1 = XSpowerlaw('p1') model = rsp(self.abs1 * self.p1) self.fit = Fit(data, model, CStat(), NelderMead(), Covariance())
def savefig(name): plt.savefig(name) print("# Created: {}".format(name)) basedir = get_datadir() if basedir is None: raise IOError("No test data directory") # Temporarily jump to the directory to get the paths right cwd = os.getcwd() os.chdir(basedir) print(f'--> jumping to {basedir}') pha = read_pha('3c273.pi') os.chdir(cwd) report('pha') report('pha.get_background()') report('pha.get_arf()') report('pha.get_rmf()') chans = np.arange(1, 1025, dtype=int) counts = np.ones(1024, dtype=int) test = DataPHA('example', chans, counts) report('test') plot = DataPHAPlot() plot.histo_prefs['linestyle'] = '-' plot.prepare(pha)
def test_pha_write_xmm_grating(make_data_path, tmp_path): """Check we can handle an XMM grating. This has an AREASCAL column and WCS attached to the CHANNEL column. We don't guarantee the WCS info will be retained or propagated. """ def check_header(obj, roundtrip=False): # check header for a selected set of keywords hdr = obj.header # selected OGIP keywords assert "HDUNAME" not in hdr assert hdr["HDUCLASS"] == "OGIP" assert hdr["HDUCLAS1"] == "SPECTRUM" assert hdr["HDUCLAS2"] == "NET" assert hdr["HDUCLAS3"] == "COUNT" if roundtrip: assert hdr["HDUCLAS4"] == "TYPE:I" else: assert "HDUCLAS4" not in hdr assert hdr["HDUVERS"] == "1.2.0" assert "HDUVERS1" not in hdr # a few header keywords to check they are handled, # including data types (string, float, integer, # logical). # assert hdr["OBJECT"] == "TW Hya" assert hdr["TELESCOP"] == "XMM" assert hdr["INSTRUME"] == "RGS1" assert hdr["FILTER"] == "UNKNOWN" assert not hdr["POISSERR"] assert hdr["CHANTYPE"] == "PI" assert hdr["DETCHANS"] == 3600 assert "EXPOSIRE" not in hdr assert hdr["SYS_ERR"] == pytest.approx(0.0) assert hdr["GROUPING"] == 0 assert "QUALITY" not in hdr assert hdr["CORRSCAL"] == 1 assert "AREASCAL" not in hdr assert "BACKSCAL" not in hdr assert hdr["CORRFILE"] == "none" for key in ["RESPFILE", "ANCRFILE", "BACKFILE"]: assert key not in hdr # WCS attached to the CHANNEL column if backend_is("crates"): assert "TCRYP1" not in hdr assert "TCRVL1" not in hdr assert "TLMIN1" not in hdr assert "TLMAX1" not in hdr elif backend_is("pyfits"): assert hdr["TCTYP1"] == "" assert hdr["TCUNI1"] == "Angstrom" assert hdr["TCRPX1"] == 1 assert hdr["TCRVL1"] == pytest.approx(4.00500011) assert hdr["TCDLT1"] == pytest.approx(0.01) assert hdr["TLMIN1"] == 1 assert hdr["TLMAX1"] == 3600 else: assert False # programming error def check_data(obj, roundtrip=False): """Basic checks of the data""" assert obj.staterror.min() == pytest.approx(1.0) assert obj.staterror.sum() == pytest.approx(4496.9490242) assert obj.staterror[1498] == pytest.approx(7.937253952) assert np.argmax(obj.staterror) == 1498 assert obj.syserror is None assert obj.bin_lo is None assert obj.bin_hi is None assert obj.exposure == pytest.approx(28965.6406250) assert obj.backscal == pytest.approx(1.0) assert len(obj.areascal) == 3600 assert obj.areascal.min() == pytest.approx(0.0) assert obj.areascal.max() == pytest.approx(1.0) assert obj.areascal.sum() == pytest.approx(2950.6953666) for f in ["grouped", "subtracted", "rate"]: assert isinstance(getattr(obj, f), bool) assert not obj.grouped assert not obj.subtracted assert obj.rate assert obj.plot_fac == 0 assert obj.channel.dtype == np.dtype("float64") assert obj.counts.dtype == np.dtype("float64") assert obj.channel == pytest.approx(np.arange(1, 3601)) assert len(obj.counts) == 3600 expected = [0, 0, -1, -3, 0, -1, -1, 0, -2, -1] assert obj.counts[112:122] == pytest.approx(expected) assert obj.grouping is None assert len(obj.quality) == 3600 assert obj.quality.min() == 0 assert obj.quality.max() == 1 assert obj.quality.sum() == 741 infile = make_data_path("xmmrgs/P0112880201R1S004SRSPEC1003.FTZ") indata = io.read_pha(infile, use_errors=True) assert isinstance(indata, DataPHA) assert indata.name.endswith("/P0112880201R1S004SRSPEC1003.FTZ") check_header(indata) check_data(indata) assert indata.response_ids == [] assert indata.background_ids == [] assert indata.get_arf() is None assert indata.get_rmf() is None assert indata.get_background() is None # check we can write it out - be explicit with all options # outfile = tmp_path / "test.pha" outfile = str(outfile) # our IO routines don't recognize paths # This is a bit ugly as I want to capture/hide the Astro deprecation # warning, but it's only valid when we are using AstroPy. # if backend_is("pyfits"): from astropy.utils.exceptions import AstropyDeprecationWarning with pytest.warns(AstropyDeprecationWarning) as ws: io.write_pha(outfile, indata, ascii=False, clobber=False) # Skip the known "deprecation" error, but flag up any other # errors. This is probably excessive (e.g. the message may change # textually) but for now see how this goes. I did not want to # add this to the generic list of known warnings as I do not like # making these affect all tests. # expected = "The following keywords are now recognized as special column-related attributes and should be set via the Column objects: TCDLTn, TCRPXn, TCRVLn, TCTYPn, TCUNIn. In future, these values will be dropped from manually specified headers automatically and replaced with values generated based on the Column objects." if len(ws) > 0: assert len(ws) == 1, len(ws) assert str(ws[0].message) == expected else: io.write_pha(outfile, indata, ascii=False, clobber=False) outdata = io.read_pha(outfile, use_errors=True) assert isinstance(outdata, DataPHA) assert outdata.name.endswith("/test.pha") check_header(outdata, roundtrip=True) check_data(outdata, roundtrip=True) # The responses and background should NOT be read in # (we are in a different directory to infile so we can't # find these files). # assert outdata.response_ids == [] assert outdata.background_ids == [] assert outdata.get_arf() is None assert outdata.get_rmf() is None assert outdata.get_background() is None
def test_pha_write_basic(make_data_path, tmp_path): """Check we can write out a PHA file as a FITS file. This uses an existing PHA file rather than creating one manually. """ def check_header(obj): # check header for a selected set of keywords hdr = obj.header # selected OGIP keywords check_hduname(hdr, "SPECTRUM") assert hdr["HDUCLASS"] == "OGIP" assert hdr["HDUCLAS1"] == "SPECTRUM" assert hdr["HDUCLAS2"] == "TOTAL" assert hdr["HDUCLAS3"] == "TYPE:I" assert hdr["HDUCLAS4"] == "COUNT" assert hdr["HDUVERS"] == "1.1.0" assert hdr["HDUVERS1"] == "1.1.0" # a few header keywords to check they are handled, # including data types (string, float, integer, # logical). # assert hdr["OBJECT"] == "3C 273" assert hdr["INSTRUME"] == "ACIS" assert hdr["GRATING"] == "HETG" assert hdr["DETNAM"] == "ACIS-56789" assert hdr["RA_NOM"] == pytest.approx(187.28186566) assert hdr["DEC_NOM"] == pytest.approx(2.05610034) assert hdr["SIM_X"] == pytest.approx(-0.68282252) assert hdr["DATE-OBS"] == "2000-01-10T06:47:15" assert isinstance(hdr["CLOCKAPP"], (bool, np.bool_)) assert hdr["CLOCKAPP"] assert hdr["TIMEZERO"] == 0 assert hdr["DETCHANS"] == 1024 assert hdr["SYS_ERR"] == pytest.approx(0.0) assert hdr["GROUPING"] == 0 assert hdr["QUALITY"] == 0 def check_data(obj, roundtrip=False): """Basic checks of the data""" assert obj.staterror is None assert obj.syserror is None assert obj.bin_lo is None assert obj.bin_hi is None assert obj.exposure == pytest.approx(38564.608926889) assert np.log10(obj.backscal) == pytest.approx(-5.597491618115439) assert obj.areascal == pytest.approx(1.0) for f in ["grouped", "subtracted", "rate"]: assert isinstance(getattr(obj, f), bool) assert obj.grouped assert not obj.subtracted assert obj.rate assert obj.plot_fac == 0 assert obj.channel.dtype == np.dtype("float64") assert obj.counts.dtype == np.dtype("float64") assert obj.channel == pytest.approx(np.arange(1, 1025)) assert len(obj.counts) == 1024 assert obj.counts[0:11] == pytest.approx(np.zeros(11)) cvals = [1, 3, 2, 3, 7, 1, 6, 4, 4, 0] assert obj.counts[12:22] == pytest.approx(cvals) assert len(obj.grouping) == 1024 if roundtrip: assert obj.quality is None else: assert len(obj.quality) == 1024 assert obj.quality == pytest.approx(np.zeros(1024)) if backend_is("crates"): assert obj.grouping.dtype == np.dtype("int16") if not roundtrip: assert obj.quality.dtype == np.dtype("int16") elif backend_is("pyfits"): assert obj.grouping.dtype == np.dtype(">i2") if not roundtrip: assert obj.quality.dtype == np.dtype(">i2") else: pytest.fail("Unrecognized IO backend") one, = np.where(obj.grouping == 1) expected = [0, 17, 21, 32, 39, 44, 48, 51, 54, 56, 59, 61, 65, 68, 71, 75, 78, 82, 88, 96, 101, 110, 116, 124, 130, 133, 139, 143, 150, 156, 164, 177, 186, 196, 211, 232, 244, 260, 276, 291, 323, 344, 368, 404, 450, 676] assert one == pytest.approx(expected) assert obj.grouping.sum() == -932 assert set(obj.grouping) == {-1, 1} infile = make_data_path("3c273.pi") indata = io.read_pha(infile) assert isinstance(indata, DataPHA) assert indata.name.endswith("/3c273.pi") check_header(indata) check_data(indata) # The responses and background should be read in # assert indata.response_ids == pytest.approx([1]) assert indata.background_ids == pytest.approx([1]) assert indata.get_arf().name.endswith("/3c273.arf") assert indata.get_rmf().name.endswith("/3c273.rmf") assert indata.get_background().name.endswith("/3c273_bg.pi") # check we can write it out - be explicit with all options # outfile = tmp_path / "test.pha" outfile = str(outfile) # our IO routines don't recognize paths io.write_pha(outfile, indata, ascii=False, clobber=False) outdata = io.read_pha(outfile) assert isinstance(outdata, DataPHA) assert outdata.name.endswith("/test.pha") check_header(outdata) check_data(outdata, roundtrip=True) # The responses and background should NOT be read in # (we are in a different directory to infile so we can't # find these files). # assert outdata.response_ids == [] assert outdata.background_ids == [] assert outdata.get_arf() is None assert outdata.get_rmf() is None assert outdata.get_background() is None
def test_write_pha_fits_with_extras_roundtrip(tmp_path, caplog): """PHA-I with grouping/quality/errors/header This covers issue #488 - that is, should the output use the correct data type for columns? At present the code does not. """ chans = np.arange(1, 5, dtype=np.int32) counts = np.asarray([1, 0, 3, 2], dtype=np.int32) grouping = np.asarray([1, -1, 1, 1], dtype=np.int16) quality = np.asarray([0, 0, 0, 2], dtype=np.int16) etime = 1023.4 bscal = 0.05 ascal = np.asarray([1, 1, 0.9, 0.9]) hdr = {"TELESCOP": "CHANDRA", "INSTRUME": "ACIS", "FILTER": "NONE", "CHANTYPE": "PI", "DETCHANS": 10, # This intentionally does not match the data "OBJECT": "Made up source", "CORRFILE": "None", # This will cause a warning when reading in the file "ANCRFILE": "made-up-ancrfile.fits", # "structural keywords" which match DETCHANS "TLMIN1": 1, "TLMAX1": 10} pha = DataPHA("testy", chans.astype(np.float64), counts.astype(np.float32), grouping=grouping.astype(np.float32), quality=quality.astype(np.float64), exposure=etime, backscal=bscal, areascal=ascal, header=hdr) outfile = tmp_path / "out.pi" io.write_pha(str(outfile), pha, ascii=False, clobber=False) pha = None assert len(caplog.record_tuples) == 0 with SherpaVerbosity("INFO"): inpha = io.read_pha(str(outfile)) assert len(caplog.record_tuples) == 1 lname, lvl, msg = caplog.record_tuples[0] assert lname == "sherpa.astro.io" assert lvl == logging.WARNING # message depends on the backend if backend_is("crates"): assert msg.startswith("File ") assert msg.endswith("/made-up-ancrfile.fits does not exist.") elif backend_is("pyfits"): assert msg.startswith("file '") assert msg.endswith("/made-up-ancrfile.fits' not found") assert isinstance(inpha, DataPHA) assert inpha.channel == pytest.approx(chans) assert inpha.counts == pytest.approx(counts) assert inpha.grouping == pytest.approx(grouping) assert inpha.quality == pytest.approx(quality) assert inpha.exposure == pytest.approx(etime) assert inpha.backscal == pytest.approx(bscal) assert inpha.areascal == pytest.approx(ascal) for field in ["staterror", "syserror", "bin_lo", "bin_hi"]: assert getattr(inpha, field) is None assert inpha.grouped assert not inpha.subtracted assert inpha.units == "channel" assert inpha.rate assert inpha.plot_fac == 0 assert inpha.response_ids == [] assert inpha.background_ids == [] if backend_is("crates"): check_write_pha_fits_with_extras_roundtrip_crates(outfile, etime, bscal) elif backend_is("pyfits"): check_write_pha_fits_with_extras_roundtrip_pyfits(outfile, etime, bscal) else: raise RuntimeError(f"Unknown io backend: {io.backend}")
def test_read_pha_errors(make_data_path): """Can we read in a ROSAT PHA file.""" infile = make_data_path(PHAFILE) pha = io.read_pha(infile, use_errors=True) validate_pha(pha, errors=True)
def test_chandra_phaII_roundtrip(make_data_path, tmp_path): """Can we read in/write out/read in a PHA-II dataset. There is no abiility to write out a PHA-II file, only PHA-I. """ def check_header(pha, roundtrip=False): hdr = pha.header assert hdr["TG_M"] == 2 assert hdr["TG_PART"] == 1 check_hduname(hdr, "SPECTRUM") assert hdr["HDUCLASS"] == "OGIP" assert hdr["HDUCLAS1"] == "SPECTRUM" assert hdr["HDUCLAS2"] == "TOTAL" assert hdr["HDUCLAS3"] == "COUNT" assert hdr["HDUCLAS4"] == "TYPE:II" assert hdr["HDUVERS"] == "1.0.0" assert hdr["HDUVERS1"] == "1.0.0" assert "HDUVERS2" not in hdr assert hdr["ORIGIN"] == "ASC" assert hdr["CREATOR"] == "tgextract - Version CIAO 4.8" assert hdr["OBJECT"] == "3C 120" assert hdr["MISSION"] == "AXAF" assert hdr["TELESCOP"] == "CHANDRA" assert hdr["INSTRUME"] == "ACIS" assert hdr["GRATING"] == "HETG" assert hdr["DETNAM"] == "ACIS-56789" assert hdr["DETCHANS"] == 8192 assert hdr["CHANTYPE"] == "PI" assert "TOTCTS" not in hdr assert not hdr["POISSERR"] if roundtrip: assert hdr["SYS_ERR"] == 0 else: assert not "SYS_ERR" in hdr assert hdr["QUALITY"] == 0 assert hdr["GROUPING"] == 0 assert hdr["CORRFILE"] == "none" assert hdr["CORRSCAL"] == pytest.approx(1.0) for key in ["ANCRFILE", "BACKFILE", "RESPFILE"]: assert key not in hdr def check_data(pha, bkg=False, roundtrip=False): assert len(pha.channel) == 8192 assert len(pha.counts) == 8192 assert pha.staterror is None assert pha.syserror is None assert len(pha.channel) == 8192 assert len(pha.channel) == 8192 assert pha.grouping is None assert pha.quality is None assert pha.exposure == pytest.approx(77716.294300039) if bkg: assert pha.backscal == pytest.approx(4.0188284) assert pha.areascal is None else: assert pha.backscal == pytest.approx(1.0) assert pha.areascal == pytest.approx(1.0) assert pha.rate assert pha.response_ids == [] expected = [] if bkg or roundtrip else [1, 2] assert pha.background_ids == expected infile = make_data_path("3c120_pha2.gz") phas = io.read_pha(infile) assert len(phas) == 12 # pick the fifth element and a few quick checks pha = phas[4] assert isinstance(pha, DataPHA) assert pha.name.endswith("/3c120_pha2.gz") check_header(pha) check_data(pha) bkg1 = pha.get_background(1) bkg2 = pha.get_background(2) check_header(bkg1) check_header(bkg1) check_data(bkg1, bkg=True) check_data(bkg2, bkg=True) outfile = tmp_path / "test.pha" io.write_pha(str(outfile), pha, ascii=False, clobber=False) outpha = io.read_pha(str(outfile)) assert isinstance(outpha, DataPHA) assert outpha.name.endswith("/test.pha") check_header(outpha, roundtrip=True) check_data(outpha, roundtrip=True)
print("{}".format(name)) print(repr(eval(name))) print("----------------------------------------") def savefig(name): outfile = os.path.join(savedir, name) plt.savefig(outfile) print("# Created: {}".format(name)) os.chdir('../../../../sherpa-test-data/sherpatest/') from sherpa.astro.io import read_pha pha = read_pha('9774.pi') dump("pha") dump("pha.get_background()") dump("pha.get_arf()") dump("pha.get_rmf()") dump("pha.header['INSTRUME']") dump("pha.header['DETNAM']") dump("pha.channel.size") pha.set_analysis('energy') pha.notice(0.3, 7) tabs = ~pha.mask pha.group_counts(20, tabStops=tabs)
y = arf.exposure * ydet / (rmf.e_max - rmf.e_min) y = y.repeat(2) plt.plot(x, y, '-') plt.yscale('log') plt.ylim(1e4, 3e6) plt.xlim(0, 10) plt.xlabel('Energy (keV)') plt.ylabel('Count / keV') savefig('rspmodelnopha_energy.png') from sherpa.astro.io import read_pha from sherpa.astro.instrument import RSPModelPHA pha2 = read_pha('3c273.pi') arf2 = pha2.get_arf() rmf2 = pha2.get_rmf() mdl2 = PowLaw1D('mdl2') inst2 = RSPModelPHA(arf2, rmf2, pha2, mdl2) report("inst2") dump("inst2([]).size") pha2.set_analysis('energy') report('pha2.get_filter()') report('pha2.get_filter_expr()') pha2.notice(0.5, 7.0) report('pha2.get_filter()')