Beispiel #1
0
    def test_norm_works(self):
        # Check that the norm parameter for additive models
        # works, as it is handled separately from the other
        # parameters.
        import sherpa.astro.xspec as xs

        # need an additive model
        mdl = xs.XSpowerlaw()
        mdl.PhoIndex = 2
        egrid = [0.1, 0.2, 0.3, 0.4]

        mdl.norm = 1.2
        y1 = mdl(egrid)

        mfactor = 2.1
        mdl.norm = mdl.norm.val * mfactor
        y2 = mdl(egrid)

        # check that the sum is not 0 and that it
        # scales as expected.
        s1 = y1.sum()
        s2 = y2.sum()
        self.assertGreater(s1, 0.0, msg='powerlaw is positive')
        self.assertAlmostEqual(s2, mfactor * s1,
                               msg='powerlaw norm scaling')
Beispiel #2
0
def test_norm_works(clean_astro_ui):
    # Check that the norm parameter for additive models
    # works, as it is handled separately from the other
    # parameters.
    import sherpa.astro.xspec as xs

    # need an additive model
    mdl = xs.XSpowerlaw()
    mdl.PhoIndex = 2
    egrid = [0.1, 0.2, 0.3, 0.4]

    mdl.norm = 1.2
    y1 = mdl(egrid)

    mfactor = 2.1
    mdl.norm = mdl.norm.val * mfactor
    y2 = mdl(egrid)

    # check that the sum is not 0 and that it
    # scales as expected.
    s1 = y1.sum()
    s2 = y2.sum()

    assert s1 > 0.0, 'powerlaw is positive'
    assert s2 == pytest.approx(mfactor * s1)
Beispiel #3
0
    def test_checks_input_length(self):
        import sherpa.astro.xspec as xs
        mdl = xs.XSpowerlaw()

        # Check when input array is too small (< 2 elements)
        self.assertRaises(TypeError, mdl, [0.1])

        # Check when input arrays are not the same size (when the
        # low and high bin edges are given)
        self.assertRaises(TypeError, mdl, [0.1, 0.2, 0.3], [0.2, 0.3])
        self.assertRaises(TypeError, mdl, [0.1, 0.2], [0.2, 0.3, 0.4])
Beispiel #4
0
def test_xspec_expression():
    """Check we can enforce XSPEC expressions"""

    from sherpa.astro import xspec

    # pick an additive and multiplicative model
    a1 = xspec.XSpowerlaw()
    m1 = xspec.XSwabs()

    m = 2 * (a1 + m1)

    assert a1.ndim == 1
    assert m1.ndim == 1
    assert m.ndim == 1
Beispiel #5
0
def test_checks_input_length(clean_astro_ui):
    import sherpa.astro.xspec as xs
    mdl = xs.XSpowerlaw()

    # Check when input array is too small (< 2 elements)
    with pytest.raises(TypeError):
        mdl([0.1])

    # Check when input arrays are not the same size (when the
    # low and high bin edges are given)
    with pytest.raises(TypeError):
        mdl([0.1, 0.2, 0.3], [0.2, 0.3])

    with pytest.raises(TypeError):
        mdl([0.1, 0.2], [0.2, 0.3, 0.4])
Beispiel #6
0
def test_xspec_convolutionmodel_requires_bin_edges_low_level():
    """Check we can not call a convolution model with a single grid (calc).

    This used to be supported in Sherpa 4.13 and before.
    """

    import sherpa.astro.xspec as xs

    m1 = xs.XSpowerlaw()
    m2 = xs.XScflux()
    mdl = m2(m1)

    emsg = r'calc\(\) requires pars,lo,hi arguments, sent 2 arguments'
    with pytest.warns(FutureWarning, match=emsg):
        mdl.calc([p.val for p in mdl.pars], [0.1, 0.2, 0.3, 0.4])
Beispiel #7
0
def test_instrument_model(make_data_path):
    """Check the full response model"""

    from sherpa.astro import xspec

    s = Session()
    s.load_pha(make_data_path('3c273.pi'))

    a1 = xspec.XSpowerlaw()
    m1 = xspec.XSwabs()

    s.set_source(m1 * a1)

    src = s.get_source()
    mdl = s.get_model()
    assert src.ndim == 1
    assert mdl.ndim == 1
Beispiel #8
0
def test_cflux_nbins():
    """Check that the number of bins created by cflux is correct.

    The test_cflux_calc_xxx routines do include a test of the number
    of bins, but that is just for a Data1DInt dataset, so the model
    only ever gets called with explicit lo and hi edges. This test
    calls the model directly to check both 1 and 2 argument variants.

    Notes
    -----
    There's no check of a non-contiguous grid.
    """

    from sherpa.astro import xspec

    spl = PowLaw1D('sherpa')
    xpl = xspec.XSpowerlaw('xspec')

    spl.gamma = 0.7
    xpl.phoindex = 0.7

    egrid = np.arange(0.1, 2, 0.01)
    elo = egrid[:-1]
    ehi = egrid[1:]

    nbins = elo.size

    def check_bins(lbl, mdl):
        y1 = mdl(egrid)
        y2 = mdl(elo, ehi)

        assert y1.size == nbins + 1, '{}: egrid'.format(lbl)
        assert y2.size == nbins, '{}: elo/ehi'.format(lbl)

    # verify assumptions
    #
    check_bins('Sherpa model', spl)
    check_bins('XSPEC model', xpl)

    # Now try the convolved versions
    #
    cflux = xspec.XScflux("conv")
    check_bins('Convolved Sherpa', cflux(spl))
    check_bins('Convolved XSPEC', cflux(xpl))
def test_calc_xspec_regrid():
    """Test the CFLUX convolution model calculations (XSPEC model)

    Can we regrid the model and get a similar result to the
    direct version? This uses zashift but with 0 redshift.

    See Also
    --------
    test_calc_sherpa_regrid

    """

    from sherpa.astro import xspec

    mdl = xspec.XSpowerlaw('xspec')
    mdl.phoindex = 1.7
    mdl.norm = 0.025

    # rather than use the 'cflux' convolution model, try
    # the redshift model but with 0 redshift.
    #
    kern = xspec.XSzashift('zshift')
    kern.redshift = 0
    mdl_convolved = kern(mdl)

    d = setup_data(elo=0.2, ehi=5, ebin=0.01)

    dr = np.arange(0.1, 7, 0.005)
    mdl_regrid = mdl_convolved.regrid(dr[:-1], dr[1:])

    yconvolved = d.eval_model(mdl_convolved)
    yregrid = d.eval_model(mdl_regrid)

    # Do a per-pixel comparison (this will automatically catch any
    # difference in the output sizes).
    #
    ydiff = np.abs(yconvolved - yregrid)
    mdiff = ydiff.max()
    # rdiff = ydiff / yconvolved

    # in testing see the max difference being ~ 3e-17
    assert mdiff < 1e-15, \
        'can rebin an XSPEC powerlaw: max diff={}'.format(mdiff)
def test_cflux_nbins():
    """Check that the number of bins created by cflux is correct.

    The test_cflux_calc_xxx routines do include a test of the number
    of bins, but that is just for a Data1DInt dataset, so the model
    only ever gets called with explicit lo and hi edges. Now that we
    no-longer support evaluating the model with a single grid this
    test may not add much power, but leave in for now.

    Notes
    -----
    There's no check of a non-contiguous grid.

    """

    from sherpa.astro import xspec

    spl = PowLaw1D('sherpa')
    xpl = xspec.XSpowerlaw('xspec')

    spl.gamma = 0.7
    xpl.phoindex = 0.7

    egrid = np.arange(0.1, 2, 0.01)
    elo = egrid[:-1]
    ehi = egrid[1:]

    nbins = elo.size

    def check_bins(lbl, mdl):
        y = mdl(elo, ehi)
        assert y.size == nbins, f'{lbl}: elo/ehi'

    # verify assumptions
    #
    check_bins('Sherpa model', spl)
    check_bins('XSPEC model', xpl)

    # Now try the convolved versions
    #
    cflux = xspec.XScflux("conv")
    check_bins('Convolved Sherpa', cflux(spl))
    check_bins('Convolved XSPEC', cflux(xpl))
def test_cflux_calc_xspec():
    """Test the CFLUX convolution model calculations (XSPEC model)

    This is a test of the convolution interface, as the results of
    the convolution can be easily checked. The model being convolved
    is an XSPEC model.

    See Also
    --------
    test_cflux_calc_sherpa

    """

    from sherpa.astro import xspec

    mdl = xspec.XSpowerlaw('xspec')
    mdl.phoindex = 1.7
    mdl.norm = 0.025
    _test_cflux_calc(mdl, mdl.phoindex.val, mdl.norm.val)
Beispiel #12
0
def test_checks_input_length():
    import sherpa.astro.xspec as xs
    mdl = xs.XSpowerlaw()

    # Check when input array is too small (< 2 elements)
    with pytest.raises(TypeError) as exc1:
        mdl([0.1], [0.2])

    assert str(
        exc1.value) == "input array must have at least 2 elements, found 1"

    # Check when input arrays are not the same size.
    with pytest.raises(TypeError) as exc2:
        mdl([0.1, 0.2, 0.3], [0.2, 0.3])

    assert str(exc2.value) == "input arrays are not the same size: 3 and 2"

    with pytest.raises(TypeError) as exc3:
        mdl([0.1, 0.2], [0.2, 0.3, 0.4])

    assert str(exc3.value) == "input arrays are not the same size: 2 and 3"
Beispiel #13
0
def test_convolution_model_cflux(clean_astro_ui):
    # Use the cflux convolution model, since this gives
    # an easily-checked result. At present the only
    # interface to these models is via direct access
    # to the functions (i.e. there are no model classes
    # providing access to this functionality).
    #
    import sherpa.astro.xspec as xs

    if not hasattr(xs._xspec, 'C_cflux'):
        pytest.skip('cflux convolution model is missing')

    # The energy grid should extend beyond the energy grid
    # used to evaluate the model, to avoid any edge effects.
    # It also makes things easier if the elo/ehi values align
    # with the egrid bins.
    elo = 0.55
    ehi = 1.45
    egrid = numpy.linspace(0.5, 1.5, 101)

    mdl1 = xs.XSpowerlaw()
    mdl1.PhoIndex = 2

    # flux of mdl1 over the energy range of interest; converting
    # from a flux in photon/cm^2/s to erg/cm^2/s, when the
    # energy grid is in keV.
    y1 = mdl1(egrid)
    idx, = numpy.where((egrid >= elo) & (egrid < ehi))

    # To match XSpec, need to multiply by (Ehi^2-Elo^2)/(Ehi-Elo)
    # which means that we need the next bin to get Ehi. Due to
    # the form of the where statement, we should be missing the
    # Ehi value of the last bin
    e1 = egrid[idx]
    e2 = egrid[idx + 1]

    f1 = 8.01096e-10 * ((e2 * e2 - e1 * e1) * y1[idx] / (e2 - e1)).sum()

    # The cflux parameters are elo, ehi, and the log of the
    # flux within this range (this is log base 10 of the
    # flux in erg/cm^2/s). The parameters chosen for the
    # powerlaw, and energy range, should have f1 ~ 1.5e-9
    # (log 10 of this is -8.8).
    lflux = -5.0
    pars = [elo, ehi, lflux]
    y2 = xs._xspec.C_cflux(pars, y1, egrid)

    assert_is_finite(y2, "cflux", "energy")

    elo = egrid[:-1]
    ehi = egrid[1:]
    wgrid = _hc / egrid
    whi = wgrid[:-1]
    wlo = wgrid[1:]

    expected = y1 * 10**lflux / f1
    numpy.testing.assert_allclose(expected, y2, err_msg='energy, single grid')

    y1 = mdl1(wgrid)
    y2 = xs._xspec.C_cflux(pars, y1, wgrid)
    assert_is_finite(y2, "cflux", "wavelength")
    numpy.testing.assert_allclose(expected,
                                  y2,
                                  err_msg='wavelength, single grid')

    expected = expected[:-1]

    y1 = mdl1(elo, ehi)
    y2 = xs._xspec.C_cflux(pars, y1, elo, ehi)
    numpy.testing.assert_allclose(expected, y2, err_msg='energy, two arrays')

    y1 = mdl1(wlo, whi)
    y2 = xs._xspec.C_cflux(pars, y1, wlo, whi)
    numpy.testing.assert_allclose(expected,
                                  y2,
                                  err_msg='wavelength, two arrays')
Beispiel #14
0
def simulate_data_old(obs_time, gamma, d):
    """
    Simulate data with a power law using sherpa/XSPEC
    Power law will be normalized according to the observation time and will have the power law index
    Returns: energy bins, spectrum with pile up, spectrum without pile up, raw counts
    """
    #Load fake PHA data to get energy bins
    #datadir = "/Users/mlazz/Dropbox/UW/PileupABC/PileupABC/Margaret/"
    #ui.load_data(id="p1", filename=datadir+"fake_acis.pha")
    #d = ui.get_data("p1")
    bin_lo = d.bin_lo
    bin_hi = d.bin_hi
    bin_mid = bin_lo + (bin_hi - bin_lo) / 2.0

    #Extract counts and read in ARF and RMF
    counts = d.counts
    arf = d.get_arf()  # get out an ARF object
    rmf = d.get_rmf()  # get out an RMF object
    #Get exposure time, and energy bins from ARF
    exposure = arf.exposure  # exposure time in seconds
    specresp = arf.specresp  # the actual response in the ARF, i.e. effective area + stuff
    energ_lo = arf.energ_lo
    energ_hi = arf.energ_hi

    norm = obs_time / 1.0e7  # input power law normalization
    PhoIndex = gamma  # input power law photon index (is negative by default)

    #Use XSPEC to generate simple power law spectral model with given observation time and photon index
    p = xspec.XSpowerlaw()
    p.norm = norm
    p.PhoIndex = PhoIndex
    base_model = p(energ_lo, energ_hi)
    #Apply ARF and RMF to power law spectrum
    base_arf = arf.apply_arf(base_model) * exposure
    base_spec = rmf.apply_rmf(base_arf)

    #Implement pile up:
    #np.random.seed(200) # set the seed for reproducibility
    nphot = np.random.poisson(
        np.sum(base_spec)
    )  #number of photons is Poisson distributed around expected value

    tstart = 0
    tend = tstart + exposure
    phot_times = np.random.uniform(tstart, tend, size=nphot)
    phot_times = np.sort(phot_times)

    spec_pdf = base_spec / np.sum(base_spec)
    phot_energies = np.random.choice(energ_lo,
                                     size=nphot,
                                     replace=True,
                                     p=spec_pdf)

    frametime = 3.2
    intervals = np.arange(
        tstart, tend + frametime,
        frametime)  #set the times that the detector will be read
    #Apply pileup with binned_statistic which counts up number of photons that arrive within a read time
    summed_erg, bins, bin_idx = scipy.stats.binned_statistic(phot_times,
                                                             phot_energies,
                                                             bins=intervals,
                                                             statistic="sum")
    n_per_bin = np.bincount(
        bin_idx - 1)  # bin_idx is one-indexed for reasons I don't understand!
    phot_per_bin, bins2 = np.histogram(phot_times, bins=intervals)
    summed_erg = summed_erg[(summed_erg > 0) & (summed_erg < np.max(energ_hi))]
    energ_intervals = np.hstack([energ_lo, energ_hi[-1]])
    spec_nopileup, spec_bins = np.histogram(phot_energies,
                                            bins=energ_intervals)
    spec_pileup, spec_bins = np.histogram(summed_erg, bins=energ_intervals)

    pileup_fraction = len(n_per_bin[n_per_bin > 1]) / len(n_per_bin)
    #print("%i percent of the frames have piled events in them."%(pileup_fraction*100))

    return energ_intervals, spec_pileup, spec_nopileup, d.counts
Beispiel #15
0
def test_convolution_model_cflux():
    """This tests the low-level interfce of the convolution model"""

    # Use the cflux convolution model, since this gives
    # an easily-checked result.
    #
    import sherpa.astro.xspec as xs

    if not hasattr(xs._xspec, 'C_cflux'):
        pytest.skip('cflux convolution model is missing')

    # The energy grid should extend beyond the energy grid
    # used to evaluate the model, to avoid any edge effects.
    # It also makes things easier if the elo/ehi values align
    # with the egrid bins.
    elo = 0.55
    ehi = 1.45
    egrid = numpy.linspace(0.5, 1.5, 101)
    eg1 = egrid[:-1]
    eg2 = egrid[1:]

    mdl1 = xs.XSpowerlaw()
    mdl1.PhoIndex = 2

    # flux of mdl1 over the energy range of interest; converting
    # from a flux in photon/cm^2/s to erg/cm^2/s, when the
    # energy grid is in keV.
    y1 = mdl1(eg1, eg2)
    idx, = numpy.where((egrid >= elo) & (egrid < ehi))

    # To match XSpec, need to multiply by (Ehi^2-Elo^2)/(Ehi-Elo)
    # which means that we need the next bin to get Ehi. Due to
    # the form of the where statement, we should be missing the
    # Ehi value of the last bin
    e1 = egrid[idx]
    e2 = egrid[idx + 1]

    f1 = 8.01096e-10 * ((e2 * e2 - e1 * e1) * y1[idx] / (e2 - e1)).sum()

    # The cflux parameters are elo, ehi, and the log of the
    # flux within this range (this is log base 10 of the
    # flux in erg/cm^2/s). The parameters chosen for the
    # powerlaw, and energy range, should have f1 ~ 1.5e-9
    # (log 10 of this is -8.8).
    lflux = -5.0
    pars = [elo, ehi, lflux]

    y1_a = numpy.zeros(y1.size + 1)
    y1_a[:-1] = y1
    y2_a = xs._xspec.C_cflux(pars, y1_a, egrid)
    y2_b = xs._xspec.C_cflux(pars, y1, eg1, eg2)

    assert y2_a[:-1] == pytest.approx(y2_b)
    assert y2_a[-1] == 0.0

    assert_is_finite(y2_b, "cflux", "energy")

    elo = egrid[:-1]
    ehi = egrid[1:]
    wgrid = _hc / egrid
    whi = wgrid[:-1]
    wlo = wgrid[1:]

    expected = y1 * 10**lflux / f1
    assert y2_b == pytest.approx(expected)

    y1 = mdl1(elo, ehi)
    y2 = xs._xspec.C_cflux(pars, y1, elo, ehi)
    assert y2 == pytest.approx(expected)

    y1 = mdl1(wlo, whi)
    y2 = xs._xspec.C_cflux(pars, y1, wlo, whi)
    assert y2 == pytest.approx(expected)