Esempio n. 1
0
def test_calc_flux_pha_unabsorbed(make_data_path, clean_astro_ui):
    """Can we calculate an unabsorbed flux?"""

    # The idea is that with a model expression of
    #    const1d.scale * powlaw1d.pl
    # when scale is not 1 (and not integrated) then we can
    # just look to see if the "absorbed" flux is scale * the
    # "unabsorbed" flux.
    #
    infile = make_data_path('3c273.pi')
    ui.load_pha(infile)

    scale = ui.create_model_component('const1d', 'scale')
    pl = ui.create_model_component('powlaw1d', 'pl')

    scale.c0 = 0.8
    scale.integrate = False
    pl.gamma = 1.5
    pl.ampl = 1e-4

    ui.set_source(scale * pl)

    pflux_abs = ui.calc_photon_flux(0.5, 7)
    pflux_unabs = ui.calc_photon_flux(0.5, 7, model=pl)

    eflux_abs = ui.calc_energy_flux(0.5, 7)
    eflux_unabs = ui.calc_energy_flux(0.5, 7, model=pl)

    pflux_scale = pflux_abs / pflux_unabs
    eflux_scale = eflux_abs / eflux_unabs

    assert pflux_scale == pytest.approx(0.8)
    assert eflux_scale == pytest.approx(0.8)
Esempio n. 2
0
def test_calc_flux_pha_analysis(elo, ehi, setting, lo, hi, make_data_path,
                                clean_astro_ui):
    """Do calc_photon/energy_flux return the expected results: fluxes + analysis setting

    Basic test for different analysis settings: the
    same range (modulo precision of conversion) gives the
    same results.
    """

    infile = make_data_path('3c273.pi')
    pl = ui.create_model_component('powlaw1d', 'pl')

    ui.load_pha(infile)
    ui.set_source(pl)

    pflux = ui.calc_photon_flux(elo, ehi)
    eflux = ui.calc_energy_flux(elo, ehi)

    ui.set_analysis(setting)
    pflux2 = ui.calc_photon_flux(lo, hi)
    eflux2 = ui.calc_energy_flux(lo, hi)

    # use approx here since the bin edges are not guaranteed
    # to line up, and use a large tolerance.
    #
    assert pflux2 == pytest.approx(pflux, rel=1e-2)

    eflux = np.log10(eflux)
    eflux2 = np.log10(eflux2)
    assert eflux2 == pytest.approx(eflux, rel=1e-3)
Esempio n. 3
0
    def test_pha_intro(self):
        self.run_thread('pha_intro')
        # astro.ui imported as ui, instead of
        # being in global namespace
        self.assertEqualWithinTol(ui.get_fit_results().statval, 37.9079, 1e-4)
        self.assertEqualWithinTol(ui.get_fit_results().rstat, 0.902569, 1e-4)
        self.assertEqualWithinTol(ui.get_fit_results().qval, 0.651155, 1e-4)
        self.assertEqualWithinTol(self.locals['p1'].gamma.val, 2.15852, 1e-4)
        self.assertEqualWithinTol(self.locals['p1'].ampl.val, 0.00022484, 1e-4)

        self.assertEqualWithinTol(ui.calc_photon_flux(), 0.000469964, 1e-4)
        self.assertEqualWithinTol(ui.calc_energy_flux(), 9.614847e-13, 1e-4)
        self.assertEqualWithinTol(ui.calc_data_sum(), 706.85714092, 1e-4)
        self.assertEqualWithinTol(ui.calc_model_sum(), 638.45693377, 1e-4)
        self.assertEqualWithinTol(ui.calc_source_sum(), 0.046996409, 1e-4)
        self.assertEqualWithinTol(
            ui.eqwidth(self.locals['p1'], ui.get_source()), -0.57731725, 1e-4)
        self.assertEqualWithinTol(
            ui.calc_kcorr([1, 1.2, 1.4, 1.6, 1.8, 2], 0.5, 2), [
                0.93341286, 0.93752836, 0.94325233, 0.94990140, 0.95678054,
                0.96393515
            ], 1e-4)

        self.assertEqual(ui.get_fit_results().nfev, 22)
        self.assertEqual(ui.get_fit_results().numpoints, 44)
        self.assertEqual(ui.get_fit_results().dof, 42)
Esempio n. 4
0
        def cmp_pha_intro(fit_result, p1, covarerr):
            assert fit_result.statval == approx(37.9079, rel=1e-4)
            assert fit_result.rstat == approx(0.902569, rel=1e-4)
            assert fit_result.qval == approx(0.651155, rel=1e-4)
            self.assertEqual(fit_result.nfev, 22)
            self.assertEqual(fit_result.numpoints, 44)
            self.assertEqual(fit_result.dof, 42)
            p1.gamma.val == approx(2.15852, rel=1e-4)
            p1.ampl.val == approx(0.00022484, rel=1e-4)

            assert ui.calc_photon_flux() == approx(0.000469964, rel=1e-4)
            assert ui.calc_energy_flux() == approx(9.614847e-13, rel=1e-4)
            assert ui.calc_data_sum() == approx(706.85714092, rel=1e-4)
            assert ui.calc_model_sum() == approx(638.45693377, rel=1e-4)
            assert ui.calc_source_sum() == approx(0.046996409, rel=1e-4)

            calc = ui.eqwidth(self.locals['p1'], ui.get_source())
            assert calc == approx(-0.57731725, rel=1e-4)

            calc = ui.calc_kcorr([1, 1.2, 1.4, 1.6, 1.8, 2], 0.5, 2)
            # Prior to fixing #619 the expected values were
            # expected = [0.93341286, 0.93752836, 0.94325233,
            #             0.94990140, 0.95678054, 0.96393515]
            expected = [0.93132747, 0.9352768, 0.94085917,
                        0.94738472, 0.95415463, 0.96121113]
            assert calc == approx(expected, rel=1e-4)
Esempio n. 5
0
    def test_pha_intro(self):
        self.run_thread("pha_intro")
        # astro.ui imported as ui, instead of
        # being in global namespace
        self.assertEqualWithinTol(ui.get_fit_results().statval, 37.9079, 1e-4)
        self.assertEqualWithinTol(ui.get_fit_results().rstat, 0.902569, 1e-4)
        self.assertEqualWithinTol(ui.get_fit_results().qval, 0.651155, 1e-4)
        self.assertEqualWithinTol(self.locals["p1"].gamma.val, 2.15852, 1e-4)
        self.assertEqualWithinTol(self.locals["p1"].ampl.val, 0.00022484, 1e-4)

        self.assertEqualWithinTol(ui.calc_photon_flux(), 0.000469964, 1e-4)
        self.assertEqualWithinTol(ui.calc_energy_flux(), 9.614847e-13, 1e-4)
        self.assertEqualWithinTol(ui.calc_data_sum(), 706.85714092, 1e-4)
        self.assertEqualWithinTol(ui.calc_model_sum(), 638.45693377, 1e-4)
        self.assertEqualWithinTol(ui.calc_source_sum(), 0.046996409, 1e-4)
        self.assertEqualWithinTol(ui.eqwidth(self.locals["p1"], ui.get_source()), -0.57731725, 1e-4)
        self.assertEqualWithinTol(
            ui.calc_kcorr([1, 1.2, 1.4, 1.6, 1.8, 2], 0.5, 2),
            [0.93341286, 0.93752836, 0.94325233, 0.94990140, 0.95678054, 0.96393515],
            1e-4,
        )

        self.assertEqual(ui.get_fit_results().nfev, 22)
        self.assertEqual(ui.get_fit_results().numpoints, 44)
        self.assertEqual(ui.get_fit_results().dof, 42)
Esempio n. 6
0
def save_conf(filename, energy_flux=None, absorbpars=[]):
    '''
    Parameters
    ----------
    filename : string
    energy_flux : string or integer
        Identifier of Sherpa model for which the energy flux will be calculated.
    absorbpars : sherpa parameter instances
        These parameters are set to 0 before the flux is calculated.
    '''
    confres = ui.get_conf_results()
    fitres = ui.get_fit_results()

    if energy_flux is not None:
        oldval = [getattr(a, 'val') for a in absorbpars]
        for a in absorbpars:
            setattr(a, 'val', 0.)
        energy_flux = ui.calc_energy_flux(0.3, 9., energy_flux)
        for a, v in zip(absorbpars, oldval):
            setattr(a, 'val', v)

    with open(join(articlepath, filename), 'w') as f:
        json.dump(
            {
                'name': confres.parnames,
                'val': confres.parvals,
                'up': confres.parmaxes,
                'down': confres.parmins,
                'redchi2': fitres.rstat,
                'dof': fitres.dof,
                'flux': energy_flux
            }, f)
Esempio n. 7
0
    def cmp_thread(fit_result, p1, covarerr):
        assert fit_result.statval == approx(37.9079, rel=1e-4)
        assert fit_result.rstat == approx(0.902569, rel=1e-4)
        assert fit_result.qval == approx(0.651155, rel=1e-4)
        assert fit_result.nfev == 22
        assert fit_result.numpoints == 44
        assert fit_result.dof == 42
        p1.gamma.val == approx(2.15852, rel=1e-4)
        p1.ampl.val == approx(0.00022484, rel=1e-4)

        assert ui.calc_photon_flux() == approx(0.000469964, rel=1e-4)
        assert ui.calc_energy_flux() == approx(9.614847e-13, rel=1e-4)
        assert ui.calc_data_sum() == approx(706.85714092, rel=1e-4)
        assert ui.calc_model_sum() == approx(638.45693377, rel=1e-4)
        assert ui.calc_source_sum() == approx(0.046996409, rel=1e-4)

        calc = ui.eqwidth(p1, ui.get_source())
        assert calc == approx(-0.57731725, rel=1e-4)

        calc = ui.calc_kcorr([1, 1.2, 1.4, 1.6, 1.8, 2], 0.5, 2)
        expected = [
            0.93132747, 0.9352768, 0.94085917, 0.94738472, 0.95415463,
            0.96121113
        ]
        assert calc == approx(expected, rel=1e-4)
Esempio n. 8
0
    def test_pha_intro(self):
        self.run_thread('pha_intro')
        # astro.ui imported as ui, instead of
        # being in global namespace
        fit_results = ui.get_fit_results()
        covarerr = sqrt(fit_results.extra_output['covar'].diagonal())
        assert covarerr[0] == approx(0.0790393, rel=1e-4)
        assert covarerr[1] == approx(1.4564e-05, rel=1e-4)
        assert fit_results.statval == approx(37.9079, rel=1e-4)
        assert fit_results.rstat == approx(0.902569, rel=1e-4)
        assert fit_results.qval == approx(0.651155, rel=1e-4)
        assert self.locals['p1'].gamma.val == approx(2.15852, rel=1e-4)
        assert self.locals['p1'].ampl.val == approx(0.00022484, rel=1e-4)

        assert ui.calc_photon_flux() == approx(0.000469964, rel=1e-4)
        assert ui.calc_energy_flux() == approx(9.614847e-13, rel=1e-4)
        assert ui.calc_data_sum() == approx(706.85714092, rel=1e-4)
        assert ui.calc_model_sum() == approx(638.45693377, rel=1e-4)
        assert ui.calc_source_sum() == approx(0.046996409, rel=1e-4)

        calc = ui.eqwidth(self.locals['p1'], ui.get_source())
        assert calc == approx(-0.57731725, rel=1e-4)

        calc = ui.calc_kcorr([1, 1.2, 1.4, 1.6, 1.8, 2], 0.5, 2)
        expected = [0.93341286, 0.93752836, 0.94325233,
                    0.94990140, 0.95678054, 0.96393515]
        assert calc == approx(expected, rel=1e-4)

        self.assertEqual(ui.get_fit_results().nfev, 22)
        self.assertEqual(ui.get_fit_results().numpoints, 44)
        self.assertEqual(ui.get_fit_results().dof, 42)
Esempio n. 9
0
def test_calc_flux_pha_density_bin_edges(clean_astro_ui):
    """What happens when filter edges partially overlap bins? flux density

    Later tests may also cover this condition, but here we use
    faked data that is made to make the behavior "obvious".
    """

    chans = np.arange(1, 11, 1, dtype=np.int)
    counts = np.zeros(chans.size, dtype=np.int)

    # "perfect" response
    energies = np.arange(1, 12, 1)
    elo, ehi = energies[:-1], energies[1:]
    flat = np.ones(chans.size, dtype=np.int)

    d = ui.DataPHA('example', chans, counts)
    arf = ui.create_arf(elo, ehi, flat)
    rmf = ui.create_rmf(elo,
                        ehi,
                        e_min=elo,
                        e_max=elo,
                        startchan=1,
                        fname=None)

    d.set_arf(arf)
    d.set_rmf(rmf)
    ui.set_data(1, d)

    ui.set_source(ui.powlaw1d.pl)
    pl.ampl = 1e-4
    pl.gamma = 1.7

    # choose an energy that is not equal to the center of the bin
    # just to check how this is handled
    #
    pdens = ui.calc_photon_flux(2.6)
    edens = ui.calc_energy_flux(2.6)

    enscale = sherpa.astro.utils._charge_e

    # Evaluate the model over the bin 2-3 keV; since the grid
    # has a width of 1 keV we do not need to divide by the bin
    # width when calculating the density.
    #
    ymdl = pl([2], [3])
    expected_pdens = ymdl.sum()
    expected_edens = enscale * 2.5 * expected_pdens

    # Prior to fixing #619, Sherpa returns 0 for both densities
    #
    assert pdens == pytest.approx(expected_pdens)

    # check against log as values ~ 5e-13
    edens = np.log10(edens)
    expected_edens = np.log10(expected_edens)
    assert edens == pytest.approx(expected_edens)
Esempio n. 10
0
def fit_multiplets(multipletlist, id = None, outpath = None, plot = False, delta_lam = .2):
    #
    n_lines =  np.sum([len(mult['wave']) for mult in multipletlist])
    result = np.zeros(n_lines, dtype = {'names': ['multname', 'linename', 'wave', 'flux', 'errup', 'errdown', 'photons', 'photonflux'], 'formats': ['S30', 'S30', 'f4', 'f4', 'f4', 'f4', 'f4', 'f4']})
    currentline = 0
    #
    for mult in multipletlist:
        if outpath is not None:
            outfile = os.path.join(outpath, filter(lambda x: x.isalnum(), mult['name']))
        else:
            outfile = None
        fit_lines(mult, id, delta_lam = delta_lam, plot = plot, outfile = outfile)
        #    
        ui.conf(id)
        conf_res = ui.get_conf_results()
        source = ui.get_source(id)
        #set all ampl to 0 and only for 1 line to real value
        set_all_val('c0', 0., id)
        for lname, lfili, lwave in zip(mult['linename'], mult['fililiname'], mult['wave']):
            print 'Fitting line '+str(currentline+1)+'/'+str(n_lines)
            par = ui.get_model_component(lfili)
            indconf_res = ( np.array(conf_res.parnames) == lfili+'.ampl').nonzero()[0]
            set_all_val('ampl', 0., id)
            
            par.ampl.val = conf_res.parvals[indconf_res]
            counts = ui.calc_model_sum(None, None, id)
            #
            print(lname, counts)
            photonflux = ui.calc_photon_flux(None, None, id)
            # determine scaling between ampl and flux
            par.ampl.val = 1
            amp2flux = ui.calc_energy_flux(None, None, id)

            par.ampl.val = conf_res.parvals[indconf_res]
            #
            val = conf_res.parvals[indconf_res] * amp2flux
            errdown = conf_res.parmins[indconf_res] * amp2flux if conf_res.parmins[indconf_res] else np.nan
            errup  = conf_res.parmaxes[indconf_res] * amp2flux if conf_res.parmaxes[indconf_res] else np.nan
            #
            result[currentline] = (mult['name'], lname, lwave, val, errup, errdown, counts, photonflux)
            #
            currentline +=1
    return result
Esempio n. 11
0
def _calc_fluxes(ids, z=0, dataScale=None, nsims=10):
    flux = np.full((len(ids), 3), np.nan)
    rf_flux = np.full((len(ids), 3), np.nan)
    rf_int_flux = np.full((len(ids), 3), np.nan)
    fx = np.full((3, 3), np.nan)

    if dataScale is None:
        # Estimate average flux of all detectors, with no errors
        for i, sid in enumerate(ids):
            # Observed flux at 0.2-12 keV obs. frame (no abs. corr.)
            flux[i, 0] = shp.calc_energy_flux(id=sid, lo=0.2, hi=12.0)

            # Observed flux at 2-10 keV rest frame (only Gal. abs. corr.)
            shp.set_par("absgal.nH", val=0)
            rf_flux[i, 0] = shp.calc_energy_flux(id=sid,
                                                 lo=2.0 / (1 + z),
                                                 hi=10.0 / (1 + z))

            # Observed flux at 2-10 keV rest frame (abs. corr.)
            shp.set_par("abs1.nH", val=0)
            rf_int_flux[i, 0] = shp.calc_energy_flux(id=sid,
                                                     lo=2.0 / (1 + z),
                                                     hi=10.0 / (1 + z))
        fx[0, 0] = np.mean(flux[:, 0])
        fx[1, 0] = np.mean(rf_flux[:, 0])
        fx[2, 0] = np.mean(rf_int_flux[:, 0])

    else:
        # Estimate average flux of all detectors sampling num times the
        # distribution of parameters assuming a multigaussian
        int_component = po1
        obs_component = abs1 * po1

        for i, sid in enumerate(ids):
            # Observed flux at 0.2-12 keV obs. frame (no abs. corr.)
            F = shp.sample_flux(lo=0.2,
                                hi=12,
                                id=sid,
                                num=nsims,
                                scales=dataScale,
                                Xrays=True)
            flux[i, :] = F[0]

            # Observed flux at 2-10 keV rest frame (only Gal. abs. corr.)
            F = shp.sample_flux(
                obs_component,
                lo=2 / (1 + z),
                hi=10 / (1 + z),
                id=sid,
                num=nsims,
                scales=dataScale,
                Xrays=True,
            )
            rf_flux[i, :] = F[1]

            # Observed flux at 2-10 keV rest frame (abs. corr.)
            F = shp.sample_flux(
                int_component,
                lo=2 / (1 + z),
                hi=10 / (1 + z),
                id=sid,
                num=nsims,
                scales=dataScale,
                Xrays=True,
            )
            rf_int_flux[i, :] = F[1]

        fx[0, 0] = np.average(flux[:, 0], weights=flux[:, 1]**-1)
        fx[1, 0] = np.average(rf_flux[:, 0], weights=rf_flux[:, 1]**-1)
        fx[2, 0] = np.average(rf_int_flux[:, 0], weights=rf_int_flux[:, 1]**-1)

        k = np.sqrt(len(ids))
        fx[0, 1:] = k / np.sum(flux[:, 2]**-1), k / np.sum(flux[:, 1]**-1)
        fx[1,
           1:] = k / np.sum(rf_flux[:, 2]**-1), k / np.sum(rf_flux[:, 1]**-1)
        fx[2, 1:] = (
            k / np.sum(rf_int_flux[:, 2]**-1),
            k / np.sum(rf_int_flux[:, 1]**-1),
        )

    return fx
Esempio n. 12
0
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)
Esempio n. 13
0
    myplot['nogal'] = pl.modelplot.y.tolist()

    set_full_model(id, get_bkg_model(id) * get_bkg_scale(id))
    pl = get_fit_plot(id)
    myplot['background'] = pl.modelplot.y.tolist()
    set_stat('cstat')
    json.dump(myplot, open('%s/fit.json' % prefix, 'w'))
    print('stored fit plot data into fit.json')

# compute 2-10keV intrinsic luminosities?
# calculate restframe intrinsic flux
set_model(id, torus)

print("estimating intrinsic flux/luminosity ...")
r = []
for row in tqdm.tqdm(solver.results['samples']):
    z = redshift.val if hasattr(redshift, 'val') else redshift
    for p, v in zip(parameters, row):
        if p.name == 'redshift' or p.name == 'z':
            z = v
        p.val = v
    srcnh.val = 20
    r.append([
        z,
        calc_energy_flux(id=id, lo=2 / (1 + z), hi=10 / (1 + z)),
        calc_energy_flux(id=id, lo=elo / (1 + z), hi=ehi / (1 + z))
    ])

numpy.savetxt(prefix + "/intrinsic_photonflux.dist", r)
print("stored intrinsic flux/luminosity data in intrinsic_photonflux.dist")
Esempio n. 14
0
def test_calc_flux_pha_bin_edges(clean_astro_ui):
    """What happens when filter edges partially overlap bins?

    Later tests may also cover this condition, but here we use
    faked data that is made to make the behavior "obvious".
    """

    chans = np.arange(1, 11, 1, dtype=np.int)
    counts = np.zeros(chans.size, dtype=np.int)

    # "perfect" response
    energies = np.arange(1, 12, 1)
    elo, ehi = energies[:-1], energies[1:]
    flat = np.ones(chans.size, dtype=np.int)

    d = ui.DataPHA('example', chans, counts)
    arf = ui.create_arf(elo, ehi, flat)
    rmf = ui.create_rmf(elo,
                        ehi,
                        e_min=elo,
                        e_max=elo,
                        startchan=1,
                        fname=None)

    d.set_arf(arf)
    d.set_rmf(rmf)
    ui.set_data(1, d)

    ui.set_source(ui.powlaw1d.pl)
    pl.ampl = 1e-4
    pl.gamma = 1.7

    # Evaluate the model on the energy grid
    ymdl = pl(elo, ehi)

    pflux = ui.calc_photon_flux(2.6, 7.8)
    eflux = ui.calc_energy_flux(2.6, 7.8)

    enscale = sherpa.astro.utils._charge_e

    # Left in as notes:
    #
    # This are the true values. Given that the edge bins (should be)
    # using linear interpolation, how close do we get to this?
    #
    # gterm1 = 1.0 - 1.7
    # gterm2 = 2.0 - 1.7
    # true_pflux = 1e-4 * (7.8**gterm1 - 2.6**gterm1) / gterm1
    # true_eflux = enscale * 1e-4 * (7.8**gterm2 - 2.6**gterm2) / gterm2
    #
    # Comparing the linear interpolation scheme to that used by
    # Sherpa prior to fixing #619, I find
    #
    # Photon fluxes:
    #     linear_interp / true_pflux = 1.042251
    #     sherpa        / true_pflux = 0.837522
    #
    # Energy fluxes:
    #     linear_interp / true_eflux = 1.017872
    #     sherpa        / true_eflux = 0.920759
    #
    scale = np.asarray([0.0, 0.4, 1.0, 1.0, 1.0, 1.0, 0.8, 0, 0.0, 0.0])
    expected_pflux = (ymdl * scale).sum()

    emid = enscale * (elo + ehi) / 2
    expected_eflux = (emid * ymdl * scale).sum()

    assert pflux == pytest.approx(expected_pflux)

    # check against log as values ~ 3e-13
    eflux = np.log10(eflux)
    expected_eflux = np.log10(expected_eflux)
    assert eflux == pytest.approx(expected_eflux)
Esempio n. 15
0
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)
Esempio n. 16
0
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)