def fit(self, conf = False): ui.ignore(None, None) ui.notice(self.start, self.stop) self.set_source() ui.fit(1) if conf: ui.conf() res = ui.get_fit_results() for line in (self.H2lines + self.nonH2lines): sourcename = line['source'].split('.')[1] print sourcename for p in ['pos', 'fwhm', 'ampl']: n = '{0}.{1}'.format(sourcename, p) _place_val(line, p, ui.get_par(n).val) self.const = ui.get_par('c1.c0').val self.redchi2 = res.rstat if conf: res = ui.get_conf_results() for line in (self.H2lines + self.nonH2lines): sourcename = line['source'].split('.')[1] for p in ['pos', 'fwhm', 'ampl']: n = '{0}.{1}'.format(sourcename, p) parmin, parmax = _parminmax(res, n) line[p+'_max'] = parmax line[p+'_min'] = parmin # deal with error on const parmin, parmax = _parminmax(res, 'c1.c0') self.const_min = parmin self.const_max = parmax
def test_background(): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) kT_sim = 1.0 Z_sim = 0.0 norm_sim = 4.0e-2 nH_sim = 0.04 redshift = 0.01 exp_time = (200., "ks") area = (1000., "cm**2") wcs = create_dummy_wcs() abs_model = WabsModel(nH_sim) events = EventList.create_empty_list(exp_time, area, wcs) spec_model = TableApecModel(0.05, 12.0, 5000, thermal_broad=False) spec = spec_model.return_spectrum(kT_sim, Z_sim, redshift, norm_sim) new_events = events.add_background(spec_model.ebins, spec, prng=prng, absorb_model=abs_model) new_events = ACIS_I(new_events, rebin=False, convolve_psf=False, prng=prng) new_events.write_spectrum("background_evt.pi", clobber=True) os.system("cp %s ." % new_events.parameters["ARF"]) os.system("cp %s ." % new_events.parameters["RMF"]) load_user_model(mymodel, "wapec") add_user_pars("wapec", ["nH", "kT", "metallicity", "redshift", "norm"], [0.01, 4.0, 0.2, redshift, norm_sim*0.8], parmins=[0.0, 0.1, 0.0, -20.0, 0.0], parmaxs=[10.0, 20.0, 10.0, 20.0, 1.0e9], parfrozen=[False, False, False, True, False]) load_pha("background_evt.pi") set_stat("cstat") set_method("simplex") ignore(":0.5, 8.0:") set_model("wapec") fit() set_covar_opt("sigma", 1.6) covar() res = get_covar_results() assert np.abs(res.parvals[0]-nH_sim) < res.parmaxes[0] assert np.abs(res.parvals[1]-kT_sim) < res.parmaxes[1] assert np.abs(res.parvals[2]-Z_sim) < res.parmaxes[2] assert np.abs(res.parvals[3]-norm_sim) < res.parmaxes[3] os.chdir(curdir) shutil.rmtree(tmpdir)
def test_background(): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) kT_sim = 1.0 Z_sim = 0.0 norm_sim = 4.0e-2 nH_sim = 0.04 redshift = 0.01 exp_time = (200., "ks") area = (1000., "cm**2") fov = (10.0, "arcmin") prng = 24 agen = ApecGenerator(0.05, 12.0, 5000, broadening=False) spec = agen.get_spectrum(kT_sim, Z_sim, redshift, norm_sim) spec.apply_foreground_absorption(norm_sim) events = make_background(area, exp_time, fov, (30.0, 45.0), spec, prng=prng) events.write_simput_file("bkgnd", overwrite=True) instrument_simulator("bkgnd_simput.fits", "bkgnd_evt.fits", exp_time, "sq_acisi_cy19", [30.0, 45.0], overwrite=True, foreground=False, ptsrc_bkgnd=False, instr_bkgnd=False, prng=prng) write_spectrum("bkgnd_evt.fits", "background_evt.pi", overwrite=True) os.system("cp %s %s ." % (arf.filename, rmf.filename)) load_user_model(mymodel, "wapec") add_user_pars("wapec", ["nH", "kT", "metallicity", "redshift", "norm"], [0.01, 4.0, 0.2, redshift, norm_sim*0.8], parmins=[0.0, 0.1, 0.0, -20.0, 0.0], parmaxs=[10.0, 20.0, 10.0, 20.0, 1.0e9], parfrozen=[False, False, False, True, False]) load_pha("background_evt.pi") set_stat("cstat") set_method("simplex") ignore(":0.5, 8.0:") set_model("wapec") fit() res = get_fit_results() assert np.abs(res.parvals[0]-nH_sim)/nH_sim < 0.1 assert np.abs(res.parvals[1]-kT_sim)/kT_sim < 0.05 assert np.abs(res.parvals[2]-Z_sim) < 0.05 assert np.abs(res.parvals[3]-norm_sim)/norm_sim < 0.05 os.chdir(curdir) shutil.rmtree(tmpdir)
def test_point_source(): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) nH_sim = 0.02 norm_sim = 1.0e-4 alpha_sim = 0.95 redshift = 0.02 exp_time = (100., "ks") area = (3000., "cm**2") spec = Spectrum.from_powerlaw(alpha_sim, redshift, norm_sim, emin=0.1, emax=11.5, nbins=2000) spec.apply_foreground_absorption(nH_sim, model="tbabs") positions = [(30.01, 45.0)] events = make_point_sources(area, exp_time, positions, (30.0, 45.0), spec, prng=prng) events.write_simput_file("ptsrc", overwrite=True) instrument_simulator("ptsrc_simput.fits", "ptsrc_evt.fits", exp_time, "sq_aciss_cy20", [30.0, 45.0], overwrite=True, foreground=False, ptsrc_bkgnd=False, instr_bkgnd=False, prng=prng) write_spectrum("ptsrc_evt.fits", "point_source_evt.pi", overwrite=True) os.system("cp %s %s ." % (arf.filename, rmf.filename)) load_user_model(mymodel, "tplaw") add_user_pars("tplaw", ["nH", "norm", "redshift", "alpha"], [0.02, norm_sim*0.8, redshift, 0.9], parmins=[0.0, 0.0, 0.0, 0.1], parmaxs=[10.0, 1.0e9, 10.0, 10.0], parfrozen=[True, False, True, False]) load_pha("point_source_evt.pi") set_stat("cstat") set_method("simplex") ignore(":0.4, 9.0:") set_model("tplaw") fit() res = get_fit_results() assert np.abs(res.parvals[0]-norm_sim)/norm_sim < 0.05 assert np.abs(res.parvals[1]-alpha_sim)/alpha_sim < 0.05 os.chdir(curdir) shutil.rmtree(tmpdir)
def test_point_source(): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) nH_sim = 0.02 norm_sim = 1.0e-4 alpha_sim = 0.95 redshift = 0.02 exp_time = (100., "ks") area = (3000., "cm**2") spec = Spectrum.from_powerlaw(alpha_sim, redshift, norm_sim, emin=0.1, emax=11.5, nbins=2000) spec.apply_foreground_absorption(nH_sim, model="tbabs") positions = [(30.01, 45.0)] events = make_point_sources(area, exp_time, positions, (30.0, 45.0), spec, prng=prng) events.write_simput_file("ptsrc", overwrite=True) instrument_simulator("ptsrc_simput.fits", "ptsrc_evt.fits", exp_time, "sq_aciss_cy19", [30.0, 45.0], overwrite=True, foreground=False, ptsrc_bkgnd=False, instr_bkgnd=False, prng=prng) write_spectrum("ptsrc_evt.fits", "point_source_evt.pi", overwrite=True) os.system("cp %s %s ." % (arf.filename, rmf.filename)) load_user_model(mymodel, "tplaw") add_user_pars("tplaw", ["nH", "norm", "redshift", "alpha"], [0.02, norm_sim*0.8, redshift, 0.9], parmins=[0.0, 0.0, 0.0, 0.1], parmaxs=[10.0, 1.0e9, 10.0, 10.0], parfrozen=[True, False, True, False]) load_pha("point_source_evt.pi") set_stat("cstat") set_method("simplex") ignore(":0.4, 9.0:") set_model("tplaw") fit() res = get_fit_results() assert np.abs(res.parvals[0]-norm_sim)/norm_sim < 0.05 assert np.abs(res.parvals[1]-alpha_sim)/alpha_sim < 0.05 os.chdir(curdir) shutil.rmtree(tmpdir)
def test_can_use_pspc_data(make_data_path): """A basic check that we can read in and use the ROSAT PSPC data. Unlike the previous tests, that directly access the io module, this uses the ui interface. """ if not six.PY2 and (backend == "crates"): pytest.skip('Python3 and Crates: known to fail') # The PSPC PHA file does not have the ANCRFILE/RESPFILE keywords # set up, so the responses has to be manually added. # ui.load_pha(make_data_path(PHAFILE), use_errors=True) assert ui.get_analysis() == 'channel' ui.load_rmf(make_data_path(RMFFILE)) assert ui.get_analysis() == 'energy' ui.set_source(ui.powlaw1d.pl) ui.set_par('pl.gamma', 1.7) ui.set_par('pl.ampl', 2e-6) s = ui.get_stat_info()[0] assert s.numpoints == 63 assert s.dof == 61 # Value obtained from XSPEC 12.9.1p; Sherpa returns # sexpected = 973.2270845920297 sexpected = 973.23 assert_allclose(s.statval, sexpected, rtol=0, atol=0.005) # apply an energy filter to remove the "bogus" points ui.ignore(None, 0.05) s = ui.get_stat_info()[0] assert s.numpoints == 62 assert s.dof == 60 assert_allclose(s.statval, sexpected, rtol=0, atol=0.005) ui.ignore(2.01, None) s = ui.get_stat_info()[0] assert s.numpoints == 7 assert s.dof == 5 assert_allclose(s.statval, sexpected, rtol=0, atol=0.005)
def test_save_filter_ignored(bid): """Does save_filter error out if everything is masked? We should be able to write out the filter in this case, as it's easy (all False). """ ui.load_arrays(1, [1, 2, 3], [5, 4, 3], ui.DataPHA) bkg = ui.DataPHA('bkg', np.asarray([1, 2, 3]), [1, 1, 0]) ui.set_bkg(bkg) ui.ignore(None, None) with pytest.raises(DataErr) as exc: ui.save_filter("temp-file-that-should-not-be-created", bkg_id=bid) assert str(exc.value) == "mask excludes all data"
def test_set_filter_mismatch_with_filter(f, bid): """Does set_filter error when there's a mis-match after a filter? test_set_filter_mismatch checks when .mask is a scalar, so now check when it's a NumPy array. """ ui.load_arrays(1, [1, 2, 3], [5, 4, 3], ui.DataPHA) bkg = ui.DataPHA('bkg', np.asarray([1, 2, 3]), [1, 1, 0]) ui.set_bkg(bkg) ui.ignore(3, None) # set the .mask attribute to an array with pytest.raises(DataErr) as exc: ui.set_filter(f, bkg_id=bid) assert str(exc.value) == 'size mismatch between 3 and 2'
def test_can_use_pspc_data(make_data_path): """A basic check that we can read in and use the ROSAT PSPC data. Unlike the previous tests, that directly access the io module, this uses the ui interface. """ # The PSPC PHA file does not have the ANCRFILE/RESPFILE keywords # set up, so the responses has to be manually added. # ui.load_pha(make_data_path(PHAFILE), use_errors=True) assert ui.get_analysis() == 'channel' ui.load_rmf(make_data_path(RMFFILE)) assert ui.get_analysis() == 'energy' ui.set_source(ui.powlaw1d.pl) ui.set_par('pl.gamma', 1.7) ui.set_par('pl.ampl', 2e-6) s = ui.get_stat_info()[0] assert s.numpoints == 63 assert s.dof == 61 # Value obtained from XSPEC 12.9.1p; Sherpa returns # sexpected = 973.2270845920297 sexpected = 973.23 assert_allclose(s.statval, sexpected, rtol=0, atol=0.005) # apply an energy filter to remove the "bogus" points ui.ignore(None, 0.05) s = ui.get_stat_info()[0] assert s.numpoints == 62 assert s.dof == 60 assert_allclose(s.statval, sexpected, rtol=0, atol=0.005) ui.ignore(2.01, None) s = ui.get_stat_info()[0] assert s.numpoints == 7 assert s.dof == 5 assert_allclose(s.statval, sexpected, rtol=0, atol=0.005)
def test_filter_bad_ungrouped(make_data_path, clean_astro_ui): """Check behavior when the data is ungrouped. This is a test of the current behavior, to check that values still hold. It may be necessary to change this test if we change the quality handling. """ infile = make_data_path('q1127_src1_grp30.pi') ui.load_pha(infile) pha = ui.get_data() assert pha.quality_filter is None assert pha.mask is True assert ui.get_dep().shape == (439, ) ui.ungroup() assert ui.get_dep().shape == (1024, ) assert pha.quality_filter is None assert pha.mask is True ui.ignore_bad() assert ui.get_dep().shape == (1024, ) assert pha.quality_filter is None expected = np.ones(1024, dtype=bool) expected[996:1025] = False assert pha.mask == pytest.approx(expected) # At this point we've changed the mask array so Sherpa thinks # we've applied a filter, so a notice is not going to change # anything. See issue #1169 # ui.notice(0.5, 7) assert pha.mask == pytest.approx(expected) # We need to ignore to change the mask. # ui.ignore(None, 0.5) ui.ignore(7, None) expected[0:35] = False expected[479:1025] = False assert pha.mask == pytest.approx(expected)
def validate_xspec_result(l, h, npts, ndof, statval): """Check that the stat results match those from XSPEC. This assumes the first data set returned by get_stat_info should be used. """ ui.notice(None, None) ui.ignore(None, l) ui.ignore(h, None) ui.ignore_bad() sinfo = ui.get_stat_info()[0] assert sinfo.numpoints == npts assert sinfo.dof == ndof # XSPEC displays results to ~ 2dp, so the tolerance # is quite forgiving here. Or I could use pyxspec to # calculate and display this. # assert_allclose(sinfo.statval, statval, rtol=0, atol=0.005)
def loadBkgPCA(id=1, readFrom='bkgPCA.json'): """ load PCA background from """ filter0 = ui.get_filter() with open(readFrom, 'r') as f: parDict = json.load(f) parDictRed = { k.split('.')[1]: v for k, v in parDict.items() if k.startswith('pca') } fitter = PCAFitter(id=id) bkgModel = PCAModel('pca{:d}'.format(id), data=fitter.pca) for p in bkgModel.pars: p.val = parDictRed[p.name] idrsp = get_identity_response(id) ui.set_bkg_full_model(id, idrsp(bkgModel)) ui.ignore() ui.notice(filter0)
def load(self, filename): self.modelFile = filename with open(filename, 'r') as f: self.pca = json.load(f) for k, v in self.pca.items(): self.pca[k] = np.array(v) nactivedata = self.pca['ihi'] - self.pca['ilo'] assert self.pca['hi'].shape == ( nactivedata, ), 'spectrum has different number of channels: %d vs %s' % (len( self.pca['hi']), self.ndata) assert self.pca['lo'].shape == self.pca['hi'].shape assert self.pca['mean'].shape == self.pca['hi'].shape assert len(self.pca['components']) == nactivedata assert nactivedata <= self.ndata ilo = int(self.pca['ilo']) ihi = int(self.pca['ihi']) self.cts = self.data[ilo:ihi] self.ncts = self.cts.sum( ) # 'have ncts background counts for deconvolution self.x = np.arange(ihi - ilo) self.ilo = ilo self.ihi = ihi # Only notice the channels between ilo + 1 and ihi (channel starts from 1, while index from 0). # The stat value will be affected, for assessment of goodness-of-fit for background. self.grouping0 = ui.get_grouping() ui.set_analysis('channel') # The channel filters, filter0 and filter_chan are all native channels. # ui.get_filter() will, instead, reture the binned channels if the spectrum is grouped. if self.grouping0 is not None: ui.ungroup() self.filter0 = ui.get_filter() ui.ignore() ui.notice( self.ilo + 1, self.ihi ) # ui.notice(a, b), from channel a to channel b, including channels a, b. self.filter_chan = ui.get_filter() ui.set_analysis('energy') if self.grouping0 is not None: ui.group()
def setup_order_plot(make_data_path): """Set up a faked dataset with multiple orders.""" pha = make_data_path('3c273.pi') ui.load_pha(pha) # It has already loaded in one response arf = ui.get_arf(resp_id=1) arf.specresp *= 0.5 for order, scale in enumerate([0.4, 0.25], 2): ui.load_arf(make_data_path('3c273.arf'), resp_id=order) ui.load_rmf(make_data_path('3c273.rmf'), resp_id=order) arf = ui.get_arf(resp_id=order) arf.specresp *= scale ui.set_source(ui.powlaw1d.pl) ui.notice(0.5, 7) ui.ignore(3, 4)
def assert_staterr(use_errors): assert np.all( ui.get_data("phacounts").counts == pytest.approx( ui.get_data("pharate").counts)) if use_errors is True: assert np.all( ui.get_data("phacounts").staterror == pytest.approx( ui.get_data("pharate").staterror)) else: assert ui.get_data("phacounts").staterror is None assert ui.get_data("pharate").staterror is None for n in ['phacounts', 'pharate']: ui.group_bins(n, 16) ui.set_analysis('energy') ui.ignore(None, 3.) if use_errors is True: assert np.all( ui.get_data("phacounts").get_error() == pytest.approx( ui.get_data("pharate").get_error())) else: assert ui.get_data("phacounts").get_error() is None assert ui.get_data("pharate").get_error() is None
def fit_lines(linelist, id = None, delta_lam = .2, plot = False, outfile = None): mymodel = filili.multilinemanager.GaussLines('const1d', id = id, baseline = baseline) linelist['fililiname'] = [''] * len(linelist['linename']) for i in range(len(linelist['linename'])): lname = linelist['linename'][i] lwave = linelist['wave'][i] previouslines = set(mymodel.line_name_list()) mymodel.add_line(linename = filter(lambda x: x.isalnum(), lname), pos = lwave) linenamelist = mymodel.line_name_list() newline = (set(linenamelist) - previouslines).pop() linelist['fililiname'][i] = newline if i ==0: firstline = linenamelist[0] ui.get_model_component(firstline).pos.max = lwave + delta_lam/10. ui.get_model_component(firstline).pos.min = lwave - delta_lam/10. else: dl = lwave - linelist['wave'][0] ui.link(ui.get_model_component(newline).pos, ui.get_model_component(firstline).pos + dl) #ui.set_analysis("wave") ui.ignore(None, None) #ignores all data ui.notice(min(linelist['wave'])-delta_lam, max(linelist['wave']) + delta_lam) ui.fit(id) if plot: ui.plot_fit(id) if has_chips: pychips.set_curve("crv1",["err.*","true"]) pychips.set_plot_title(linelist['name']) if outfile is not None: pychips.print_window(outfile, ['clobber','true']) elif has_mpl: plt.title(linelist['name']) if outfile is not None: plt.savefig(outfile) else: raise NoPlottingSystemError("Neither pychips nor matplotlib are found.")
def fitBkgPCA(id=1): """ The source model should be specified. Known Issues: While the photon indexes resulting from Wstat and PCA-based background fitting are consistent for MOS1 and MOS2, the photon index resulting from PCA-based background fitting for pn seems to be biased (steeper) by 0.04-0.05. The PCA-based model overestimates the background in the hard band. """ bkgmodel = PCAFitter(id=id) # An id has to be assigned. bkgmodel.fit() ui.set_analysis('channel') ui.ignore() ui.notice(bkgmodel.filter_chan) ui.set_analysis('energy') m, sig = cstatDist(id=id, bkg_id=1) bkgmodel.cstatM = m bkgmodel.cstatS = sig ui.set_analysis('channel') ui.ignore() ui.notice(bkgmodel.filter0) ui.set_analysis('energy') return bkgmodel
def _filter_data(self): """Filter the data. Use a slightly-complex filter - e.g. not just the end points - to exercise the system. """ ui.ignore(None, 0.5) ui.ignore(3, 4) ui.ignore(7, None)
load_pha(id, filename) try: assert get_rmf(id).energ_lo[0] > 0 assert get_arf(id).energ_lo[0] > 0 assert (get_bkg(id).counts > 0).sum() > 0 except: traceback.print_exc() sys.exit(0) set_xlog() set_ylog() set_stat('cstat') set_xsabund('wilm') set_xsxsect('vern') set_analysis(id, 'ener', 'counts') ignore(None, elo) ignore(ehi, None) notice(elo, ehi) prefix = filename + '_xagnfitter_out_' #import json #z = float(open(filename + '.z').read()) #galnh_value = float(open(filename + '.nh').read()) galabso = auto_galactic_absorption() galabso.nH.freeze() if args.backgroundmodel == 'chandra': print('calling singlefitter...') fitter = SingleFitter(id, filename, ChandraBackground)
def plaw_fit(alpha_sim): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) bms = BetaModelSource() ds = bms.ds def _hard_emission(field, data): return YTQuantity(1.0e-18, "s**-1*keV**-1")*data["density"]*data["cell_volume"]/mp ds.add_field(("gas", "hard_emission"), function=_hard_emission, units="keV**-1*s**-1") nH_sim = 0.02 abs_model = WabsModel(nH_sim) A = YTQuantity(2000., "cm**2") exp_time = YTQuantity(2.0e5, "s") redshift = 0.01 sphere = ds.sphere("c", (100.,"kpc")) plaw_model = PowerLawSourceModel(1.0, 0.01, 11.0, "hard_emission", alpha_sim, prng=prng) photons = PhotonList.from_data_source(sphere, redshift, A, exp_time, plaw_model) D_A = photons.parameters["FiducialAngularDiameterDistance"] dist_fac = 1.0/(4.*np.pi*D_A*D_A*(1.+redshift)**3).in_cgs() norm_sim = float((sphere["hard_emission"]).sum()*dist_fac.in_cgs())*(1.+redshift) events = photons.project_photons("z", absorb_model=abs_model, prng=bms.prng, no_shifting=True) events = ACIS_I(events, rebin=False, convolve_psf=False, prng=bms.prng) events.write_spectrum("plaw_model_evt.pi", clobber=True) os.system("cp %s ." % events.parameters["ARF"]) os.system("cp %s ." % events.parameters["RMF"]) load_user_model(mymodel, "wplaw") add_user_pars("wplaw", ["nH", "norm", "redshift", "alpha"], [0.01, norm_sim*1.1, redshift, 0.9], parmins=[0.0, 0.0, 0.0, 0.1], parmaxs=[10.0, 1.0e9, 10.0, 10.0], parfrozen=[False, False, True, False]) load_pha("plaw_model_evt.pi") set_stat("cstat") set_method("simplex") ignore(":0.6, 7.0:") set_model("wplaw") fit() set_covar_opt("sigma", 1.645) covar() res = get_covar_results() assert np.abs(res.parvals[0]-nH_sim) < res.parmaxes[0] assert np.abs(res.parvals[1]-norm_sim) < res.parmaxes[1] assert np.abs(res.parvals[2]-alpha_sim) < res.parmaxes[2] os.chdir(curdir) shutil.rmtree(tmpdir)
def plaw_fit(alpha_sim, prng=None): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) bms = BetaModelSource() ds = bms.ds if prng is None: prng = bms.prng def _hard_emission(field, data): return YTQuantity( 1.0e-18, "s**-1*keV**-1") * data["density"] * data["cell_volume"] / mp ds.add_field(("gas", "hard_emission"), function=_hard_emission, units="keV**-1*s**-1") nH_sim = 0.02 A = YTQuantity(2000., "cm**2") exp_time = YTQuantity(2.0e5, "s") redshift = 0.01 sphere = ds.sphere("c", (100., "kpc")) plaw_model = PowerLawSourceModel(1.0, 0.01, 11.0, "hard_emission", alpha_sim, prng=prng) photons = PhotonList.from_data_source(sphere, redshift, A, exp_time, plaw_model) D_A = photons.parameters["fid_d_a"] dist_fac = 1.0 / (4. * np.pi * D_A * D_A * (1. + redshift)**3).in_cgs() norm_sim = float( (sphere["hard_emission"]).sum() * dist_fac.in_cgs()) * (1. + redshift) events = photons.project_photons("z", [30., 45.], absorb_model="wabs", nH=nH_sim, prng=bms.prng, no_shifting=True) events.write_simput_file("plaw", overwrite=True) instrument_simulator("plaw_simput.fits", "plaw_evt.fits", exp_time, "sq_acisi_cy19", [30.0, 45.0], overwrite=True, foreground=False, ptsrc_bkgnd=False, instr_bkgnd=False, prng=prng) write_spectrum("plaw_evt.fits", "plaw_model_evt.pi", overwrite=True) os.system("cp %s %s ." % (arf.filename, rmf.filename)) load_user_model(mymodel, "wplaw") add_user_pars("wplaw", ["nH", "norm", "redshift", "alpha"], [0.01, norm_sim * 1.1, redshift, 0.9], parmins=[0.0, 0.0, 0.0, 0.1], parmaxs=[10.0, 1.0e9, 10.0, 10.0], parfrozen=[False, False, True, False]) load_pha("plaw_model_evt.pi") set_stat("cstat") set_method("simplex") ignore(":0.6, 7.0:") set_model("wplaw") fit() res = get_fit_results() assert np.abs(res.parvals[0] - nH_sim) / nH_sim < 0.1 assert np.abs(res.parvals[1] - norm_sim) / norm_sim < 0.05 assert np.abs(res.parvals[2] - alpha_sim) / alpha_sim < 0.05 os.chdir(curdir) shutil.rmtree(tmpdir)
def test_plot_order_multi(make_data_path, clean_astro_ui): """Rather than fake data, use a known dataset. Here we pretend we have three orders but with the same response (except that the ARF is 0.5, 0.4, 0.25 of the normal ARF). """ pha = make_data_path('3c273.pi') ui.load_pha(pha) # It has already loaded in one response arf = ui.get_arf(resp_id=1) arf.specresp *= 0.5 for order, scale in enumerate([0.4, 0.25], 2): ui.load_arf(make_data_path('3c273.arf'), resp_id=order) ui.load_rmf(make_data_path('3c273.rmf'), resp_id=order) arf = ui.get_arf(resp_id=order) arf.specresp *= scale ui.set_source(ui.powlaw1d.pl) ui.notice(0.5, 7) ui.ignore(3, 4) fplot = ui.get_fit_plot() oplot = ui.get_order_plot() # The idea is to compare the X range of plot_fit to plot_order # (but accessed just using the plot objects rather than creating # an actual plot). # # First some safety checks assert fplot.dataplot.xlo == pytest.approx(fplot.modelplot.xlo) assert fplot.dataplot.xhi == pytest.approx(fplot.modelplot.xhi) assert len(oplot.xlo) == 3 assert len(oplot.xhi) == 3 assert len(oplot.y) == 3 assert oplot.xlo[1] == pytest.approx(oplot.xlo[0]) assert oplot.xlo[2] == pytest.approx(oplot.xlo[0]) assert oplot.xhi[1] == pytest.approx(oplot.xhi[0]) assert oplot.xhi[2] == pytest.approx(oplot.xhi[0]) # We know the y values are 0.5, 0.4, 0.25 times the original arf # so we can compare them. # assert oplot.y[1] == pytest.approx(oplot.y[0] * 0.4 / 0.5) assert oplot.y[2] == pytest.approx(oplot.y[0] * 0.25 / 0.5) xlo = oplot.xlo[0] xhi = oplot.xhi[0] assert len(xlo) == 564 assert xlo[0] == pytest.approx(0.46720001101493835) assert xhi[-1] == pytest.approx(9.869600296020508) # The model plot is technically drawn the same way as the order plot # (ungrouped) but it uses different code (sherpa.astro.plot.ModelHistogram) # so let's compare. # mplot = ui.get_model_plot() assert mplot.xlo[0] == pytest.approx(0.46720001101493835) assert mplot.xhi[-1] == pytest.approx(9.869600296020508) # Also compare to the fit plot (which is grouped) # assert fplot.modelplot.xlo[0] == pytest.approx(0.46720001101493835) assert fplot.modelplot.xhi[-1] == pytest.approx(9.869600296020508)
def test_xspec_con_ui_cflux(make_data_path, clean_astro_ui, restore_xspec_settings): """Check cflux from the UI layer with a response.""" from sherpa.astro import xspec infile = make_data_path('3c273.pi') ui.load_pha('random', infile) ui.subtract('random') ui.ignore(None, 0.5) ui.ignore(7, None) ui.set_source('random', 'xsphabs.gal * xscflux.sflux(powlaw1d.pl)') mdl = ui.get_source('random') assert mdl.name == '(xsphabs.gal * xscflux.sflux(powlaw1d.pl))' assert len(mdl.pars) == 7 assert mdl.pars[0].fullname == 'gal.nH' assert mdl.pars[1].fullname == 'sflux.Emin' assert mdl.pars[2].fullname == 'sflux.Emax' assert mdl.pars[3].fullname == 'sflux.lg10Flux' assert mdl.pars[4].fullname == 'pl.gamma' assert mdl.pars[5].fullname == 'pl.ref' assert mdl.pars[6].fullname == 'pl.ampl' assert isinstance(mdl.lhs, xspec.XSphabs) assert isinstance(mdl.rhs, xspec.XSConvolutionModel) gal = ui.get_model_component('gal') sflux = ui.get_model_component('sflux') pl = ui.get_model_component('pl') assert isinstance(gal, xspec.XSphabs) assert isinstance(sflux, xspec.XScflux) assert isinstance(pl, PowLaw1D) # the convolution model needs the normalization to be fixed # (not for this example, as we are not fitting, but do this # anyway for reference) pl.ampl.frozen = True sflux.emin = 1 sflux.emax = 5 sflux.lg10Flux = -12.3027 pl.gamma = 2.03 gal.nh = 0.039 ui.set_xsabund('angr') ui.set_xsxsect('vern') # check we get the "expected" statistic (so this is a regression # test). # ui.set_stat('chi2gehrels') sinfo = ui.get_stat_info() assert len(sinfo) == 1 sinfo = sinfo[0] assert sinfo.numpoints == 40 assert sinfo.dof == 37 assert sinfo.statval == pytest.approx(21.25762265234619) # Do we get the same flux from Sherpa's calc_energy_flux? # cflux = ui.calc_energy_flux(id='random', model=sflux(pl), lo=1, hi=5) lcflux = np.log10(cflux) assert lcflux == pytest.approx(sflux.lg10Flux.val)
def test_background(): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) kT_sim = 1.0 Z_sim = 0.0 norm_sim = 4.0e-2 nH_sim = 0.04 redshift = 0.01 exp_time = (200., "ks") area = (1000., "cm**2") fov = (10.0, "arcmin") prng = 24 agen = ApecGenerator(0.05, 12.0, 5000, broadening=False) spec = agen.get_spectrum(kT_sim, Z_sim, redshift, norm_sim) spec.apply_foreground_absorption(norm_sim) events = make_background(area, exp_time, fov, (30.0, 45.0), spec, prng=prng) events.write_simput_file("bkgnd", overwrite=True) instrument_simulator("bkgnd_simput.fits", "bkgnd_evt.fits", exp_time, "sq_acisi_cy19", [30.0, 45.0], overwrite=True, foreground=False, ptsrc_bkgnd=False, instr_bkgnd=False, prng=prng) write_spectrum("bkgnd_evt.fits", "background_evt.pi", overwrite=True) os.system("cp %s %s ." % (arf.filename, rmf.filename)) load_user_model(mymodel, "wapec") add_user_pars("wapec", ["nH", "kT", "metallicity", "redshift", "norm"], [0.01, 4.0, 0.2, redshift, norm_sim * 0.8], parmins=[0.0, 0.1, 0.0, -20.0, 0.0], parmaxs=[10.0, 20.0, 10.0, 20.0, 1.0e9], parfrozen=[False, False, False, True, False]) load_pha("background_evt.pi") set_stat("cstat") set_method("simplex") ignore(":0.5, 8.0:") set_model("wapec") fit() res = get_fit_results() assert np.abs(res.parvals[0] - nH_sim) / nH_sim < 0.1 assert np.abs(res.parvals[1] - kT_sim) / kT_sim < 0.05 assert np.abs(res.parvals[2] - Z_sim) < 0.05 assert np.abs(res.parvals[3] - norm_sim) / norm_sim < 0.05 os.chdir(curdir) shutil.rmtree(tmpdir)
def test_vapec_beta_model(): bms = BetaModelSource() tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) prng = 45 ds = bms.ds A = 30000. exp_time = 1.0e4 redshift = 0.05 nH_sim = 0.02 sphere = ds.sphere("c", (0.5, "Mpc")) kT_sim = bms.kT Z_sim = bms.Z O_sim = bms.O Ca_sim = bms.Ca var_elem = {"O": ("stream", "oxygen"), "Ca": ("stream", "calcium")} thermal_model = ThermalSourceModel("apec", 0.1, 11.5, 20000, var_elem=var_elem, Zmet=("gas","metallicity"), prng=prng) photons = PhotonList.from_data_source(sphere, redshift, A, exp_time, thermal_model) D_A = photons.parameters["fid_d_a"] norm_sim = sphere.quantities.total_quantity("emission_measure") norm_sim *= 1.0e-14/(4*np.pi*D_A*D_A*(1.+redshift)*(1.+redshift)) norm_sim = float(norm_sim.in_cgs()) events = photons.project_photons("z", [30.0, 45.0], absorb_model="tbabs", nH=nH_sim, prng=prng, no_shifting=True) new_events = Lynx_Calorimeter(events, prng=prng) os.system("cp %s %s ." % (arf.filename, rmf.filename)) new_events.write_channel_spectrum("var_abund_beta_model_evt.pha", overwrite=True) load_user_model(mymodel_var, "tbapec") add_user_pars("tbapec", ["nH", "kT", "abund", "redshift", "norm", "O", "Ca"], [nH_sim, 4.0, Z_sim, redshift, norm_sim*0.8, 0.3, 0.5], parmins=[0.0, 0.1, 0.0, -20.0, 0.0, 0.0, 0.0], parmaxs=[10.0, 20.0, 10.0, 20.0, 1.0e9, 10.0, 10.0], parfrozen=[True, False, True, True, False, False, False]) load_pha("var_abund_beta_model_evt.pha") set_stat("cstat") set_method("levmar") ignore(":0.6, 8.0:") set_model("tbapec") fit() res = get_fit_results() assert np.abs(res.parvals[0]-kT_sim)/kT_sim < 0.05 assert np.abs(res.parvals[1]-norm_sim)/norm_sim < 0.05 assert np.abs(res.parvals[2]-O_sim)/O_sim < 0.05 assert np.abs(res.parvals[3]-Ca_sim)/Ca_sim < 0.15 os.chdir(curdir) shutil.rmtree(tmpdir)
def do_beta_model(source, v_field, em_field, axis="z", prng=None): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) if prng is None: prng = source.prng ds = source.ds A = 30000. exp_time = 1.0e4 redshift = 0.05 nH_sim = 0.02 sphere = ds.sphere("c", (0.5, "Mpc")) kT_sim = source.kT Z_sim = source.Z thermal_model = ThermalSourceModel("apec", 0.1, 11.5, 20000, Zmet=Z_sim, prng=prng) photons = PhotonList.from_data_source(sphere, redshift, A, exp_time, thermal_model) D_A = photons.parameters["fid_d_a"] norm_sim = sphere.quantities.total_quantity(em_field) norm_sim *= 1.0e-14/(4*np.pi*D_A*D_A*(1.+redshift)*(1.+redshift)) norm_sim = float(norm_sim.in_cgs()) v1, v2 = sphere.quantities.weighted_variance(v_field, em_field) if isinstance(axis, string_types): if axis == "z": fac = 1.0 else: fac = 0.0 else: axis /= np.sqrt(np.dot(axis, axis)) fac = np.dot(axis, [0.0, 0.0, 1.0]) sigma_sim = fac*float(v1.in_units("km/s")) mu_sim = -fac*float(v2.in_units("km/s")) events = photons.project_photons(axis, [30.0, 45.0], absorb_model="tbabs", nH=nH_sim, prng=prng) events.write_simput_file("beta_model", overwrite=True) instrument_simulator("beta_model_simput.fits", "beta_model_evt.fits", exp_time, "mucal", [30.0, 45.0], overwrite=True, foreground=False, ptsrc_bkgnd=False, instr_bkgnd=False, prng=prng) write_spectrum("beta_model_evt.fits", "beta_model_evt.pi", overwrite=True) os.system("cp %s %s ." % (arf.filename, rmf.filename)) load_user_model(mymodel, "tbapec") add_user_pars("tbapec", ["nH", "kT", "metallicity", "redshift", "norm", "velocity"], [0.02, 4.0, 0.2, 0.04, norm_sim*0.8, 300.0], parmins=[0.0, 0.1, 0.0, -200.0, 0.0, 0.0], parmaxs=[10.0, 20.0, 10.0, 200.0, 1.0e9, 20000.0], parfrozen=[True, False, False, False, False, False]) load_pha("beta_model_evt.pi") set_stat("cstat") set_method("levmar") ignore(":0.6, 8.0:") set_model("tbapec") fit() res = get_fit_results() redshift_sim = (1.0+mu_sim/ckms)*(1.0+redshift) - 1.0 assert np.abs(res.parvals[0]-kT_sim)/kT_sim < 0.05 assert np.abs(res.parvals[1]-Z_sim)/Z_sim < 0.05 assert np.abs(res.parvals[2]-redshift_sim)/redshift_sim < 0.05 assert np.abs(res.parvals[3]-norm_sim) < 0.05 assert np.abs(res.parvals[4]-sigma_sim) < 30.0 os.chdir(curdir) shutil.rmtree(tmpdir)
def test_calc_flux_density_pha(id, energy, make_data_path, clean_astro_ui): """Do calc_photon/energy_flux return the expected results: densities The answer should be the same when lo is set and hi is None or vice versa. The flux densities are going to be in units of <value>/cm^2/s/keV {value=photon, erg} Note: this tests the "edge" condition when lo=hi; this is not documented, but left in as a check (and perhaps it should be documented). """ infile = make_data_path('3c273.pi') # The 3c273 RMF was generated over the range 0.1 to 11 keV # (inclusive). By picking gamma = 1 the photon flux # density is ampl / e and the energy flux is scale * ampl. # However, this is the exact calculation, but the one done by # Sherpa involves calculating the model over a bin and then # dividing by that bin width, which is different enough to # the analytic formula that we use this approach here when # calculating the expected values. The bin width is 0.01 keV, # with bins starting at 0.1. # ampl = 1e-4 # flux densities: exact # pflux_exp = ampl / energy # eflux_exp = 1.602e-9 * ampl # Note that you can calculate an answer at the left edge of the grid, but # not at the right (this is either a < vs <= comparison, or numeric # issues with the maximum grid value). # # Note that the RMF emin is just above 0.1 keV, ie # 0.10000000149011612, which is why an energy of 0.1 gives # an answer of 0. Now, sao_fcmp(0.1, 0.10000000149011612, sherpa.utils.eps) # returns 0, so we could consider these two values equal, but # that would complicate the flux calculation so is (currently) not # done. # de = 0.01 if energy <= 0.1 or energy >= 11: pflux_exp = 0.0 eflux_exp = 0.0 else: # assuming a bin centered on the energy; the actual grid # is not this, but this should be close hwidth = de / 2 pflux_exp = ampl * (np.log(energy + hwidth) - np.log(energy - hwidth)) / de eflux_exp = 1.602e-9 * energy * pflux_exp pl = ui.create_model_component('powlaw1d', 'pl') pl.ampl = ampl pl.gamma = 1 if id is None: ui.load_pha(infile) ui.set_source(pl) else: ui.load_pha(id, infile) ui.set_source(id, pl) # Use a subset of the data range (to check that the calc routines # ignores them, ie uses the full 0.1 to 11 keV range. # ui.ignore(None, 0.5) ui.ignore(7, None) # Do not use named arguments, but assume positional arguments if id is None: pflux1 = ui.calc_photon_flux(energy) pflux2 = ui.calc_photon_flux(None, energy) pflux3 = ui.calc_photon_flux(energy, energy) eflux1 = ui.calc_energy_flux(energy) eflux2 = ui.calc_energy_flux(None, energy) eflux3 = ui.calc_energy_flux(energy, energy) else: pflux1 = ui.calc_photon_flux(energy, None, id) pflux2 = ui.calc_photon_flux(None, energy, id) pflux3 = ui.calc_photon_flux(energy, energy, id) eflux1 = ui.calc_energy_flux(energy, None, id) eflux2 = ui.calc_energy_flux(None, energy, id) eflux3 = ui.calc_energy_flux(energy, energy, id) eflux1 = np.log10(eflux1) eflux2 = np.log10(eflux2) eflux3 = np.log10(eflux3) # Use equality here since the numbers should be the same assert pflux1 == pflux2 assert pflux1 == pflux3 assert eflux1 == eflux2 assert eflux1 == eflux3 # Note the "large" tolerance here eflux_exp = np.log10(eflux_exp) assert pflux1 == pytest.approx(pflux_exp, rel=5e-2) assert eflux1 == pytest.approx(eflux_exp, rel=1e-3)
def check_bad_grouping(exp_xlo, exp_xhi, exp_counts, lo1, hi1, lo2, hi2): """Common tests from test_grouped_pha_all_badXXX Sending in two ranges is a bit excessive but easiest thing to implement """ cts = ui.get_counts() assert cts == pytest.approx([exp_counts]) dplot = ui.get_data_plot() assert dplot.xlo == pytest.approx([exp_xlo]) assert dplot.xhi == pytest.approx([exp_xhi]) assert dplot.y == pytest.approx([exp_counts]) # ignore all the data ui.ignore(lo1, hi1) # can still plot cts = ui.get_counts() assert cts == pytest.approx([exp_counts]) cts = ui.get_counts(filter=True) assert len(cts) == 0 dplot = ui.get_data_plot() assert len(dplot.xlo) == 0 assert len(dplot.xhi) == 0 assert len(dplot.y) == 0 # ignore does not fail # ui.ignore(lo2, hi2) # we can restore the data ui.notice(None, None) cts = ui.get_counts() assert cts == pytest.approx([exp_counts]) dplot = ui.get_data_plot() assert dplot.xlo == pytest.approx([exp_xlo]) assert dplot.xhi == pytest.approx([exp_xhi]) assert dplot.y == pytest.approx([exp_counts]) # now ignore the bad channels (ie everything) # ui.ignore_bad() cts = ui.get_counts() assert len(cts) == 0 dplot = ui.get_data_plot() assert len(dplot.xlo) == 0 assert len(dplot.xhi) == 0 assert len(dplot.y) == 0 # there's nothing to notice (this line is an example of #790) ui.notice(lo1, hi1) cts = ui.get_counts() assert len(cts) == 0 dplot = ui.get_data_plot() assert len(dplot.xlo) == 0 assert len(dplot.xhi) == 0 assert len(dplot.y) == 0
def do_beta_model(source, v_field, em_field): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) ds = source.ds A = 3000. exp_time = 1.0e5 redshift = 0.05 nH_sim = 0.02 apec_model = TableApecModel(0.1, 11.5, 20000, thermal_broad=False) abs_model = TBabsModel(nH_sim) sphere = ds.sphere("c", (0.5, "Mpc")) kT_sim = source.kT Z_sim = source.Z thermal_model = ThermalSourceModel(apec_model, Zmet=Z_sim, prng=source.prng) photons = PhotonList.from_data_source(sphere, redshift, A, exp_time, thermal_model) D_A = photons.parameters["FiducialAngularDiameterDistance"] norm_sim = sphere.quantities.total_quantity(em_field) norm_sim *= 1.0e-14 / (4 * np.pi * D_A * D_A * (1. + redshift) * (1. + redshift)) norm_sim = float(norm_sim.in_cgs()) v1, v2 = sphere.quantities.weighted_variance(v_field, em_field) sigma_sim = float(v1.in_units("km/s")) mu_sim = -float(v2.in_units("km/s")) events = photons.project_photons("z", absorb_model=abs_model, prng=source.prng) events = ACIS_I(events, rebin=False, convolve_psf=False, prng=source.prng) events.write_spectrum("beta_model_evt.pi", clobber=True) os.system("cp %s ." % events.parameters["ARF"]) os.system("cp %s ." % events.parameters["RMF"]) load_user_model(mymodel, "tbapec") add_user_pars("tbapec", ["nH", "kT", "metallicity", "redshift", "norm"], [0.01, 4.0, 0.2, redshift, norm_sim * 0.8], parmins=[0.0, 0.1, 0.0, -20.0, 0.0], parmaxs=[10.0, 20.0, 10.0, 20.0, 1.0e9], parfrozen=[False, False, False, True, False]) load_pha("beta_model_evt.pi") set_stat("cstat") set_method("simplex") ignore(":0.5, 8.0:") set_model("tbapec") fit() set_covar_opt("sigma", 1.645) covar() res = get_covar_results() assert np.abs(res.parvals[0] - nH_sim) < res.parmaxes[0] assert np.abs(res.parvals[1] - kT_sim) < res.parmaxes[1] assert np.abs(res.parvals[2] - Z_sim) < res.parmaxes[2] assert np.abs(res.parvals[3] - norm_sim) < res.parmaxes[3] os.chdir(curdir) shutil.rmtree(tmpdir)
def fit(self): # try a PCA decomposition of this spectrum initial = self.decompose() ui.set_method('neldermead') bkgmodel = PCAModel('pca%s' % self.id, data=self.pca) self.bkgmodel = bkgmodel response = get_identity_response(self.id) convbkgmodel = response(bkgmodel) ui.set_bkg_full_model(self.id, convbkgmodel) for p, v in zip(bkgmodel.pars, initial): p.val = v srcmodel = ui.get_model(self.id) ui.set_full_model(self.id, srcmodel) initial_v = self.calc_bkg_stat() # print('before fit: stat: %s' % (initial_v)) ui.fit_bkg(id=self.id) # print('fit: first full fit done') final = [p.val for p in ui.get_bkg_model(self.id).pars] # print('fit: parameters: %s' % (final)) initial_v = self.calc_bkg_stat() # print('fit: stat: %s' % (initial_v)) # lets try from zero # logf.info('fit: second full fit from zero') for p in bkgmodel.pars: p.val = 0 ui.fit_bkg(id=self.id) initial_v0 = self.calc_bkg_stat() # logf.info('fit: parameters: %s' % (final)) # logf.info('fit: stat: %s' % (initial_v0)) # pick the better starting point if initial_v0 < initial_v: # logf.info('fit: using zero-fit') initial_v = initial_v0 final = [p.val for p in ui.get_bkg_model(self.id).pars] else: # logf.info('fit: using decomposed-fit') for p, v in zip(bkgmodel.pars, final): p.val = v # start with the full fit and remove(freeze) parameters print('%d parameters, stat=%.2f' % (len(initial), initial_v)) results = [(2 * len(final) + initial_v, final, len(final), initial_v)] for i in range(len(initial) - 1, 0, -1): bkgmodel.pars[i].val = 0 bkgmodel.pars[i].freeze() ui.fit_bkg(id=self.id) final = [p.val for p in ui.get_bkg_model(self.id).pars] v = self.calc_bkg_stat() print('--> %d parameters, stat=%.2f' % (i, v)) results.insert(0, (v + 2 * i, final, i, v)) print() print('Background PCA fitting AIC results:') print('-----------------------------------') print() print('stat Ncomp AIC') for aic, params, nparams, val in results: print('%-05.1f %2d %-05.1f' % (val, nparams, aic)) aic, final, nparams, val = min(results) for p, v in zip(bkgmodel.pars, final): p.val = v for i in range(nparams): bkgmodel.pars[i].thaw() print() print('Increasing parameters again...') # now increase the number of parameters again # results = [(aic, final, nparams, val)] last_aic, last_final, last_nparams, last_val = aic, final, nparams, val for i in range(last_nparams, len(bkgmodel.pars)): next_nparams = i + 1 bkgmodel.pars[i].thaw() for p, v in zip(bkgmodel.pars, last_final): p.val = v ui.fit_bkg(id=self.id) next_final = [p.val for p in ui.get_bkg_model(self.id).pars] v = self.calc_bkg_stat() next_aic = v + 2 * next_nparams if next_aic < last_aic: # accept print('%d parameters, aic=%.2f ** accepting' % (next_nparams, next_aic)) last_aic, last_final, last_nparams, last_val = next_aic, next_final, next_nparams, v else: print('%d parameters, aic=%.2f' % (next_nparams, next_aic)) # stop if we are 3 parameters ahead what we needed if next_nparams >= last_nparams + 3: break print('Final choice: %d parameters, aic=%.2f' % (last_nparams, last_aic)) # reset to the last good solution for p, v in zip(bkgmodel.pars, last_final): p.val = v last_model = convbkgmodel for i in range(10): print('Adding Gaussian#%d' % (i + 1)) # find largest discrepancy ui.set_analysis(self.id, "ener", "rate") m = ui.get_bkg_fit_plot(self.id) y = m.dataplot.y.cumsum() z = m.modelplot.y.cumsum() diff_rate = np.abs(y - z) ui.set_analysis(self.id, "ener", "counts") m = ui.get_bkg_fit_plot(self.id) x = m.dataplot.x y = m.dataplot.y.cumsum() z = m.modelplot.y.cumsum() diff = np.abs(y - z) i = np.argmax(diff) energies = x e = x[i] print( 'largest remaining discrepancy at %.3fkeV[%d], need %d counts' % (x[i], i, diff[i])) # e = x[i] power = diff_rate[i] # lets try to inject a gaussian there g = ui.xsgaussian('g_%d_%d' % (self.id, i)) print('placing gaussian at %.2fkeV, with power %s' % (e, power)) # we work in energy bins, not energy g.LineE.min = energies[0] g.LineE.max = energies[-1] g.LineE.val = e if i > len(diff) - 2: i = len(diff) - 2 if i < 2: i = 2 g.Sigma = (x[i + 1] - x[i - 1]) g.Sigma.min = (x[i + 1] - x[i - 1]) / 3 g.Sigma.max = x[-1] - x[0] g.norm.min = power * 1e-6 g.norm.val = power convbkgmodel2 = response(g) next_model = last_model + convbkgmodel2 ui.set_bkg_full_model(self.id, next_model) ui.fit_bkg(id=self.id) next_final = [p.val for p in ui.get_bkg_model(self.id).pars] next_nparams = len(next_final) v = self.calc_bkg_stat() next_aic = v + 2 * next_nparams print('with Gaussian:', next_aic, '; change: %.1f (negative is good)' % (next_aic - last_aic)) if next_aic < last_aic: print('accepting') last_model = next_model last_aic, last_final, last_nparams, last_val = next_aic, next_final, next_nparams, v else: print('not significant, rejecting') ui.set_bkg_full_model(self.id, last_model) for p, v in zip(last_model.pars, last_final): p.val = v if v == 0: # the parameter was frozen. ui.freeze(p) break self.cstat, self.dof = self.calc_bkg_stat( dof=True) # Save the final cstat and dof (dof = ihi - ilo) self.filter_energy = ui.get_filter( ) # Save the filter for background fitting. ui.set_analysis('channel') ui.ignore() ui.notice(self.filter0) # restore filter ui.set_analysis('energy')
def test_xspec_con_ui_shift(make_data_path, clean_astro_ui, restore_xspec_settings): """Check shifted models from the UI layer with a response. There is no regrid here, so we see the issue with the upper edge of the RMF cutting off the source. """ from sherpa.astro import xspec infile = make_data_path('3c273.pi') ui.load_pha(infile) ui.subtract() ui.ignore(None, 0.5) ui.ignore(7, None) msource = ui.box1d.box + ui.const1d.bgnd ui.set_source(ui.xsphabs.gal * ui.xszashift.zsh(msource)) mdl = ui.get_source() assert mdl.name == '(xsphabs.gal * xszashift.zsh((box1d.box + const1d.bgnd)))' assert len(mdl.pars) == 6 assert mdl.pars[0].fullname == 'gal.nH' assert mdl.pars[1].fullname == 'zsh.Redshift' assert mdl.pars[2].fullname == 'box.xlow' assert mdl.pars[3].fullname == 'box.xhi' assert mdl.pars[4].fullname == 'box.ampl' assert mdl.pars[5].fullname == 'bgnd.c0' assert isinstance(mdl.lhs, xspec.XSphabs) assert isinstance(mdl.rhs, xspec.XSConvolutionModel) gal = ui.get_model_component('gal') zsh = ui.get_model_component('zsh') box = ui.get_model_component('box') bgnd = ui.get_model_component('bgnd') assert isinstance(gal, xspec.XSphabs) assert isinstance(zsh, xspec.XSzashift) assert isinstance(box, Box1D) assert isinstance(bgnd, Const1D) zsh.redshift = 1 # turn off the absorption to make the comparison easier gal.nh = 0 # pick an energy range that exceeds the RMF maximum energy (11 keV) box.xlow = 10 box.xhi = 13 box.ampl = 0.5 bgnd.c0 = 0.001 bgnd.integrate = False mplot = ui.get_source_plot() # Expect, as z = 1 # # 0.1 for E < 5 keV (10 / (1+z)) # 0 E > 5.5 keV (11 / (1+z)) due to RMF cut off # 0.6 5 - 5.5 keV # idx1 = mplot.xhi <= 5 idx2 = mplot.xlo >= 5.5 # ensure we pick the "expected" range assert idx1.sum() == 490 assert idx2.sum() == 550 assert mplot.xhi[idx1].max() == pytest.approx(5) assert mplot.xlo[idx2].min() == pytest.approx(5.5) # use the inverse of the two index arrays to ensure we are not # missing any bins. # idx3 = ~(idx1 | idx2) # The tolerance has to be relatively large otherwise things fail assert mplot.y[idx1] == pytest.approx(0.1, rel=3e-5) assert mplot.y[idx2] == pytest.approx(0) assert mplot.y[idx3] == pytest.approx(0.6, rel=1e-5)
def plaw_fit(alpha_sim): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) nH_sim = 0.02 norm_sim = 1.0e-4 redshift = 0.01 exp_time = 5.0e4 area = 40000.0 inst_name = "hdxi" spec = Spectrum.from_powerlaw(alpha_sim, redshift, norm_sim) spec.apply_foreground_absorption(nH_sim) e = spec.generate_energies(exp_time, area) pt_src = PointSourceModel(30.0, 45.0, e.size) write_photon_list("plaw_model", "plaw_model", e.flux, pt_src.ra, pt_src.dec, e, clobber=True) instrument_simulator("plaw_model_simput.fits", "plaw_model_evt.fits", exp_time, inst_name, [30.0, 45.0], astro_bkgnd=None, instr_bkgnd_scale=0.0) inst = get_instrument_from_registry(inst_name) arf = AuxiliaryResponseFile(inst["arf"]) rmf = RedistributionMatrixFile(inst["rmf"]) os.system("cp %s ." % arf.filename) os.system("cp %s ." % rmf.filename) write_spectrum("plaw_model_evt.fits", "plaw_model_evt.pha", clobber=True) load_user_model(mymodel, "wplaw") add_user_pars("wplaw", ["nH", "norm", "redshift", "alpha"], [0.01, norm_sim * 0.8, redshift, 0.9], parmins=[0.0, 0.0, 0.0, 0.1], parmaxs=[10.0, 1.0e9, 10.0, 10.0], parfrozen=[False, False, True, False]) load_pha("plaw_model_evt.pha") set_stat("cstat") set_method("simplex") ignore(":0.5, 9.0:") set_model("wplaw") fit() set_covar_opt("sigma", 1.645) covar() res = get_covar_results() assert np.abs(res.parvals[0] - nH_sim) < res.parmaxes[0] assert np.abs(res.parvals[1] - norm_sim) < res.parmaxes[1] assert np.abs(res.parvals[2] - alpha_sim) < res.parmaxes[2] os.chdir(curdir) shutil.rmtree(tmpdir)
def test_xspec_con_ui_shift_regrid(make_data_path, clean_astro_ui, restore_xspec_settings): """Check shifted models from the UI layer with a response and regrid. Unlike test_xspec_con_ui_shift, the convolution model is run on an extended grid compared to the RMF. """ from sherpa.astro import xspec infile = make_data_path('3c273.pi') ui.load_pha(infile) ui.subtract() ui.ignore(None, 0.5) ui.ignore(7, None) # Ensure the grid contains the RMF grid (0.1-11 keV). # Really we should have emax to be > 11 * (1+z) but # I purposefully pick a smaller maximum to check we # get 0 values in the output # rgrid = np.arange(0.1, 20, 0.01) rlo = rgrid[:-1] rhi = rgrid[1:] msource = ui.box1d.box + ui.const1d.bgnd csource = ui.xszashift.zsh(msource) ui.set_source(ui.xsphabs.gal * csource.regrid(rlo, rhi)) mdl = ui.get_source() # What should the string representation be? # assert mdl.name == '(xsphabs.gal * regrid1d(xszashift.zsh((box1d.box + const1d.bgnd))))' assert len(mdl.pars) == 6 assert mdl.pars[0].fullname == 'gal.nH' assert mdl.pars[1].fullname == 'zsh.Redshift' assert mdl.pars[2].fullname == 'box.xlow' assert mdl.pars[3].fullname == 'box.xhi' assert mdl.pars[4].fullname == 'box.ampl' assert mdl.pars[5].fullname == 'bgnd.c0' assert isinstance(mdl.lhs, xspec.XSphabs) assert isinstance(mdl.rhs, RegridWrappedModel) gal = ui.get_model_component('gal') zsh = ui.get_model_component('zsh') box = ui.get_model_component('box') bgnd = ui.get_model_component('bgnd') assert isinstance(gal, xspec.XSphabs) assert isinstance(zsh, xspec.XSzashift) assert isinstance(box, Box1D) assert isinstance(bgnd, Const1D) zsh.redshift = 1 # turn off the absorption to make the comparison easier gal.nh = 0 # pick an energy range that exceeds the RMF maximum energy (11 keV) box.xlow = 10 box.xhi = 13 box.ampl = 0.5 bgnd.c0 = 0.001 bgnd.integrate = False mplot = ui.get_source_plot() # Expect, as z = 1 # # 0.1 for E < 5 keV or 6.5 - 10 keV # 0.6 5 - 6.5 keV # 0 > 10 keV # idx1 = (mplot.xhi <= 5) | ((mplot.xlo >= 6.5) & (mplot.xhi <= 10)) idx2 = (mplot.xlo >= 5) & (mplot.xhi <= 6.5) idx3 = mplot.xlo >= 10 # ensure we pick the "expected" range (there are 1090 bins in the # RMF energy grid) assert idx1.sum() == 840 assert idx2.sum() == 150 assert idx3.sum() == 100 # The tolerance has to be relatively large otherwise things fail # # It appears that the very-last bin of idx1 is actually ~ 0.05, # so separate that out here. It is the last bin of the "valid" # array, and so at z=1 may only have been half-filled by the # convolution. # assert mplot.y[idx1][:-1] == pytest.approx(0.1, rel=1e-5) assert mplot.y[idx1][-1] == pytest.approx(0.05, rel=3e-5) assert mplot.y[idx2] == pytest.approx(0.6, rel=3e-5) assert mplot.y[idx3] == pytest.approx(0)
def test_point_source(): tmpdir = tempfile.mkdtemp() curdir = os.getcwd() os.chdir(tmpdir) nH_sim = 0.02 norm_sim = 1.0e-4 alpha_sim = 0.95 redshift = 0.02 exp_time = (100., "ks") area = (3000., "cm**2") wcs = create_dummy_wcs() ebins = np.linspace(0.1, 11.5, 2001) emid = 0.5*(ebins[1:]+ebins[:-1]) spec = norm_sim*(emid*(1.0+redshift))**(-alpha_sim) de = np.diff(ebins)[0] abs_model = TBabsModel(nH_sim) events = EventList.create_empty_list(exp_time, area, wcs) positions = [(30.01, 45.0)] new_events = events.add_point_sources(positions, ebins, spec, prng=prng, absorb_model=abs_model) new_events = ACIS_S(new_events, prng=prng) scalex = float(np.std(new_events['xpix'])*sigma_to_fwhm*new_events.parameters["dtheta"]) scaley = float(np.std(new_events['ypix'])*sigma_to_fwhm*new_events.parameters["dtheta"]) psf_scale = ACIS_S.psf_scale assert (scalex - psf_scale)/psf_scale < 0.01 assert (scaley - psf_scale)/psf_scale < 0.01 new_events.write_spectrum("point_source_evt.pi", clobber=True) os.system("cp %s ." % new_events.parameters["ARF"]) os.system("cp %s ." % new_events.parameters["RMF"]) load_user_model(mymodel, "tplaw") add_user_pars("tplaw", ["nH", "norm", "redshift", "alpha"], [0.01, norm_sim*0.8, redshift, 0.9], parmins=[0.0, 0.0, 0.0, 0.1], parmaxs=[10.0, 1.0e9, 10.0, 10.0], parfrozen=[False, False, True, False]) load_pha("point_source_evt.pi") set_stat("cstat") set_method("simplex") ignore(":0.5, 9.0:") set_model("tplaw") fit() set_covar_opt("sigma", 1.6) covar() res = get_covar_results() assert np.abs(res.parvals[0]-nH_sim) < res.parmaxes[0] assert np.abs(res.parvals[1]-norm_sim/de) < res.parmaxes[1] assert np.abs(res.parvals[2]-alpha_sim) < res.parmaxes[2] os.chdir(curdir) shutil.rmtree(tmpdir)
def test_calc_flux_pha(id, lo, hi, make_data_path, clean_astro_ui): """Do calc_photon/energy_flux return the expected results: fluxes? This skips those combinations where only one of lo or hi is None, since this is handle by test_calc_flux_density_pha. Flues are in units of <value>/cm^2/s {value=photon, erg} The checks are made against ranges that are chosen to cover matching the grid, a subset of the grid (with and without matching the start/end), partial overlaps, and no overlap. """ infile = make_data_path('3c273.pi') # The 3c273 RMF was generated over the range 0.1 to 11 keV # (inclusive). By picking gamma = 1 the photon-flux # integral is just ampl * (log(ehi) - log(elo)) # and the energy-flux ampl is ampl * (ehi - elo) * scale # where scale converts 1 keV to erg. # ampl = 1e-4 if lo is None: loval = 0.1 elif lo < 0.1: loval = 0.1 else: loval = lo if hi is None: hival = 11.0 elif hi > 11.0: hival = 11.0 else: hival = hi # expected fluxes; special case the handling of there being no # overlap between the user grid and the data grid. # if lo is not None and (lo > 11.0 or hi < 0.1): pflux_exp = 0.0 eflux_exp = 0.0 else: pflux_exp = ampl * (np.log(hival) - np.log(loval)) eflux_exp = 1.602e-9 * ampl * (hival - loval) pl = ui.create_model_component('powlaw1d', 'pl') pl.ampl = ampl pl.gamma = 1 if id is None: ui.load_pha(infile) ui.set_source(pl) else: ui.load_pha(id, infile) ui.set_source(id, pl) # Use a subset of the data range (to check that the calc routines # ignores them, ie uses the full 0.1 to 11 keV range. # ui.ignore(None, 0.5) ui.ignore(7, None) # Do not use named arguments, but assume positional arguments if id is None: pflux = ui.calc_photon_flux(lo, hi) eflux = ui.calc_energy_flux(lo, hi) else: pflux = ui.calc_photon_flux(lo, hi, id) eflux = ui.calc_energy_flux(lo, hi, id) # Since the energy fluxes are ~1e-12 we want to rescale the # value before comparison. Here we use a log transform. # eflux_exp = np.log10(eflux_exp) eflux = np.log10(eflux) assert pflux == pytest.approx(pflux_exp, rel=1e-3) assert eflux == pytest.approx(eflux_exp, rel=1e-4)