Exemplo n.º 1
0
def test_integrate_setting_con(clsname):
    """Can we change the integrate setting of convolution models.

    This is test_integrate_setting after wrapping the model by a
    convolution model. At present the convolution model does not have
    an integrate setting.

    Note that the convolved model doesn't make much sense physically -
    at least when it's xscflux(xswabs) - but we just care about the
    evaluation process here.

    """

    from sherpa.astro import xspec

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

    conv = xspec.XScflux()
    assert conv.integrate

    omdl = getattr(xspec, 'XS{}'.format(clsname))()
    assert omdl.integrate

    # Convolution models do not have an integrate setting
    mdl = conv(omdl)
    with pytest.raises(AttributeError):
        mdl.integrate

    y1 = mdl(elo, ehi)

    # as mdl does not have an integrate setting, just change
    # omdl
    omdl.integrate = False
    assert not omdl.integrate

    y2 = mdl(elo, ehi)

    # Assume the integrate setting is ignored. To ensure we
    # can test small values use the log of the data, and
    # as we can have zero values (from xswabs) replace them
    # with a sentinel value
    #
    y1[y1 <= 0] = 1e-10
    y2[y2 <= 0] = 1e-10

    y1 = np.log10(y1)
    y2 = np.log10(y2)
    assert y2 == pytest.approx(y1)
Exemplo n.º 2
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])
Exemplo n.º 3
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))
Exemplo n.º 4
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. 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))
Exemplo n.º 5
0
def test_cflux_settings():
    """Do the expected things happen when a model is calculated?"""

    from sherpa.astro import xspec

    kern = xspec.XScflux('cflux')
    assert isinstance(kern, xspec.XSConvolutionKernel), \
        "cflux creates XSConvolutionKernel"

    cfluxpars = [('Emin', 0.5, True, 'keV'),
                 ('Emax', 10.0, True, 'keV'),
                 ('lg10Flux', -12, False, 'cgs')]
    _check_pars('cflux', kern, cfluxpars)

    mdl = kern(PowLaw1D('pl'))
    assert isinstance(mdl, xspec.XSConvolutionModel), \
        "cflux(mdl) creates XSConvolutionModel"

    plpars = [('gamma', 1.0, False, ''),
              ('ref', 1.0, True, ''),
              ('ampl', 1.0, False, '')]
    _check_pars('model', mdl, cfluxpars + plpars)
Exemplo n.º 6
0
def _test_cflux_calc(mdl, slope, ampl):
    """Test the CFLUX convolution model calculation.

    This is a test of the convolution interface, as the results of
    the convolution can be easily checked.

    Parameters
    ----------
    mdl
        The unconvolved model. It is assumed to be a power law (so
        either a XSpowerlaw or PowLaw1D instance).
    slope, ampl : number
        The slope (gamma or PhoIndex) and amplitude
        (ampl or norm) of the power law.

    See Also
    --------
    test_cflux_calc_sherpa, test_cflux_calc_xspec

    """

    from sherpa.astro import xspec

    d = setup_data()

    kern = xspec.XScflux('cflux')

    mdl_unconvolved = mdl
    mdl_convolved = kern(mdl)

    # As it's just a power law, we can evaluate analytically
    #
    pterm = 1.0 - slope
    emin = d.xlo[0]
    emax = d.xhi[-1]
    counts_expected = ampl * (emax**pterm - emin**pterm) / \
        pterm

    # Could evaluate the model directly, but check that it's working
    # with the Sherpa setup. It is not clear that going through
    # the eval_model method really buys us much testing since the
    # main check we would really want to do is with the DataPHA
    # class, but that requires a lot of set up; the idea is that
    # the interface is abstract enough that going through
    # Data1DInt's eval_model API is sufficient.
    #
    y_unconvolved = d.eval_model(mdl_unconvolved)
    nbins_unconvolved = y_unconvolved.size
    assert nbins_unconvolved == d.xlo.size, \
        'unconvolved model has correct # bins'

    counts_unconvolved = y_unconvolved.sum()

    # This is mainly a sanity check of everything, as it is not
    # directly related to the convolution model. The counts_expected
    # term is > 0.01 with the chosen settings, so 1e-15
    # is a hand-wavy term to say "essentially zero". It may need
    # tweaking depending on platform/setup.
    #
    assert np.abs(counts_expected - counts_unconvolved) < 1e-15, \
        'can integrate a power law'

    # How to verify the convolution model? Well, one way is to
    # give it a flux, get the model values, and then check that
    # these values are very close to the expected values for
    # that flux.
    #
    kern.emin = 0.5
    kern.emax = 7.0

    desired_flux = 3.2e-14
    kern.lg10flux = np.log10(desired_flux)

    y_convolved = d.eval_model(mdl_convolved)
    nbins_convolved = y_convolved.size
    assert nbins_convolved == d.xlo.size, \
        'convolved model has correct # bins'

    # The model evaluated by mdl_convolved should have a
    # log_10(flux), over the kern.Emin to kern.Emax energy range,
    # of kern.lg10Flux, when the flux is in erg/cm^2/s (assuming
    # the model it is convolving has units of photon/cm^2/s).
    #
    # d.xlo and d.xhi are in keV, so need to convert them to
    # erg.
    #
    # Use 1 keV = 1.60218e-9 erg for the conversion, which limits
    # the accuracy (in part because I don't know how this compares
    # to the value that XSPEC uses, which could depend on the
    # version of XSPEC).
    #
    econv = 1.60218e-9
    emid = econv * (d.xlo + d.xhi) / 2.0

    y_signal = emid * y_convolved

    # Do not bother with trying to correct for any partially-filled
    # bins at the end of this range (there shouldn't be any).
    #
    idx = np.where((d.xlo >= kern.emin.val) &
                   (d.xhi <= kern.emax.val))
    flux = y_signal[idx].sum()

    # Initial tests had desired_flux = 3.2e-14 and the difference
    # ~ 1.6e-16. This has been used to determine the tolerance.
    # Note that 2e-16/3.2e-14 ~ 0.006 ie ~ 0.6%
    #
    assert np.abs(flux - desired_flux) < 2e-16, \
        'flux is not as expected'

    # The cflux model should handle any change in the model amplitude
    # (I believe; as there's no one definition of what the model
    # amplitude means in XSPEC). So, increasing the amplitude - assumed
    # to the last parameter of the input model - should result in
    # the same flux values. The unconvolved model is checked to make
    # sure it does scale (as a sanity check).
    #
    rescale = 1000.0
    mdl.pars[-1].val *= rescale
    y_unconvolved_rescaled = d.eval_model(mdl_unconvolved)
    y_convolved_rescaled = d.eval_model(mdl_convolved)

    assert_allclose(y_unconvolved, y_unconvolved_rescaled / rescale,
                    atol=0, rtol=1e-7)
    assert_allclose(y_convolved, y_convolved_rescaled,
                    atol=0, rtol=1e-7)