Пример #1
0
def test_psf1d_kernel_model(caplog):
    """Access the kernel data: subkernel=True"""

    k = Box1D()
    k.xlow = 1
    k.xhi = 3
    m = PSFModel(kernel=k)

    dfold = Data1D('fold', np.arange(10), np.zeros(10))

    with caplog.at_level(logging.INFO, logger='sherpa'):
        ans = m.get_kernel(dfold)

    assert len(caplog.records) == 1
    r = caplog.record_tuples[0]
    assert r[0] == 'sherpa.instrument'
    assert r[1] == logging.INFO
    assert r[2] == "PSF frac: 1.0"

    assert isinstance(ans, Data1D)
    assert ans.name == 'kernel'

    # integers, so treat as exact
    assert (ans.x == np.arange(10)).all()

    # box1D between 1 and 3 inclusive
    y = np.asarray([0, 1, 1, 1, 0, 0, 0, 0, 0, 0])
    assert ans.y == pytest.approx(y / y.sum())

    assert ans.staterror is None
    assert ans.syserror is None
Пример #2
0
def test_runtime_interp():
    def tst_runtime_interp(model, requested, interp):
        regrid_model = mdl.regrid(requested, interp=interp)
        yregrid = regrid_model(xgrid)
        return yregrid

    xgrid = np.arange(2, 6, 0.1)
    requested = np.arange(2.5, 5.1, 0.075)
    mdl = Box1D()
    mdl.xlow = 3.1
    mdl.xhi = 4.2
    mdl.ampl = 0.4
    yregrid = tst_runtime_interp(mdl, requested, akima.akima)
    assert 4.4 == approx(yregrid.sum())
    yregrid = tst_runtime_interp(mdl, requested, linear_interp)
    assert 4.4 == approx(yregrid.sum())
    yregrid = tst_runtime_interp(mdl, requested, neville)
    assert -5.0e6 > yregrid.sum()

    d = Data1D('tst', xgrid, np.ones_like(xgrid))
    yexpected = d.eval_model(mdl)
    requested = np.arange(2.5, 7, 0.2)
    rmdl = mdl.regrid(requested)
    ygot = d.eval_model(rmdl)
    assert ygot == approx(yexpected)
Пример #3
0
def test_low_level_regrid1d_partial_overlap(requested):
    """What happens if there is partial overlap of the grid?

    The question becomes how do we evaluate the model
    "outside" the regrid range. There's at least two
    options:

      a) set to 0
      b) use the original grid

    This test is chosen so that it holds with both
    possibilities: the model evaluates to 0 outside
    of x=3.1 - 4.2, and the partial overlaps are
    carefully chosen to always include this full
    range.

    See https://github.com/sherpa/sherpa/issues/722
    """

    # The range over which we want the model evaluated
    xgrid = np.arange(2, 6, 0.1)
    d = Data1D('tst', xgrid, np.ones_like(xgrid))

    mdl = Box1D()
    mdl.xlow = 3.1
    mdl.xhi = 4.2
    mdl.ampl = 0.4

    yexpected = d.eval_model(mdl)
    assert yexpected.min() == pytest.approx(0.0)
    assert yexpected.max() == pytest.approx(0.4)

    ygot = d.eval_model(mdl.regrid(requested))
    assert ygot == pytest.approx(yexpected)
Пример #4
0
def test_wrong_kwargs():
    xgrid = np.arange(2, 6, 0.1)
    d = Data1D('tst', xgrid, np.ones_like(xgrid))
    mdl = Box1D()
    requested = np.arange(1, 7, 0.1)
    with pytest.raises(TypeError) as excinfo:
        ygot = d.eval_model(mdl.regrid(requested, fubar='wrong_kwargs'))
    assert "unknown keyword argument: 'fubar'" in str(excinfo.value)
Пример #5
0
def test_psf1d_no_kernel():
    """Error out if there's no kernel"""

    m = PSFModel('bob')
    b = Box1D()
    with pytest.raises(PSFErr) as exc:
        m(b)

    assert "PSF kernel has not been set" == str(exc.value)
Пример #6
0
def test_cache_status_multiple(caplog):
    """Check cache_status for a multi-component model.

    Unlike test_cache_syayus_single we also have evaluated the model
    so we can check that the cache status has changed.
    """

    # The model expression includes an ArithmeticConstant model (the
    # term 2) which does not have a cache and so is ignored by
    # cache_status.
    #
    p = Polynom1D()
    b = Box1D()
    c = Const1D()
    mdl = c * (2 * p + b)

    # One model is not cached
    b._use_caching = False

    mdl([0.1, 0.2, 0.3])
    mdl([0.1, 0.2, 0.3])
    mdl([0.1, 0.2, 0.3, 0.4])

    with caplog.at_level(logging.INFO, logger='sherpa'):
        mdl.cache_status()

    assert len(caplog.records) == 3

    tokens = []
    for lname, lvl, msg in caplog.record_tuples:
        assert lname == 'sherpa.models.model'
        assert lvl == logging.INFO
        toks = msg.split()
        assert len(toks) == 9
        assert toks[1] == 'size:'
        assert toks[2] == '1'
        assert toks[3] == 'hits:'
        assert toks[5] == 'misses:'
        assert toks[7] == 'check:'
        assert toks[8] == '3'

        tokens.append(toks)

    toks = tokens[0]
    assert toks[0] == 'const1d'
    assert toks[4] == '1'
    assert toks[6] == '2'

    toks = tokens[1]
    assert toks[0] == 'polynom1d'
    assert toks[4] == '1'
    assert toks[6] == '2'

    toks = tokens[2]
    assert toks[0] == 'box1d'
    assert toks[4] == '0'
    assert toks[6] == '0'
Пример #7
0
def test_get_mins_maxes_warns(caplog):
    """What happens if min/max set outside valid range"""

    mdl = Box1D()
    mdl.xlow.set(min=0, max=100, val=50)
    mdl.xhi.set(min=200, max=300, val=250)

    # Some tests can change the logging level, so make sure
    # we have the level set correctly.
    #
    with caplog.at_level(logging.INFO, logger='sherpa'):
        mdl.thawedparmins = [10, -1e40, -10]
        mdl.thawedparmaxes = [1e40, 290, 10]

    assert mdl.thawedparmins == [10, -hugeval, -10]
    assert mdl.thawedparmaxes == [hugeval, 290, 10]

    # The handling of setting min to greater > hard_max
    # (and vice versa) isn't as easily checked, as the
    # parameter value ends up causing the code to
    # error out, so we set the parameter value to the
    # limit beforehand.
    #
    mdl.xhi.set(max=hugeval, val=hugeval)

    with caplog.at_level(logging.INFO, logger='sherpa'):
        mdl.thawedparmins = [10, 1e40, -10]
        mdl.thawedparmaxes = [1000, 1e41, 10]

    mdl.xlow.set(min=-hugeval, val=-hugeval)

    with caplog.at_level(logging.INFO, logger='sherpa'):
        mdl.thawedparmins = [-1e41, hugeval, -10]
        mdl.thawedparmaxes = [-1e40, hugeval, 10]

    assert len(caplog.records) == 6
    msgs = []
    for (log_name, log_level, message) in caplog.record_tuples:
        assert log_level == logging.WARNING
        assert log_name == 'sherpa.models.model'
        msgs.append(message)

    assert msgs[
        0] == 'value of parameter box1d.xhi minimum is below hard minimum; setting to hard minimum'
    assert msgs[
        1] == 'value of parameter box1d.xlow maximum is above hard maximum; setting to hard maximum'

    assert msgs[
        2] == 'value of parameter box1d.xhi minimum is above hard maximum; setting to hard maximum'
    assert msgs[
        3] == 'value of parameter box1d.xhi maximum is above hard maximum; setting to hard maximum'

    assert msgs[
        4] == 'value of parameter box1d.xlow minimum is below hard minimum; setting to hard minimum'
    assert msgs[
        5] == 'value of parameter box1d.xlow maximum is below hard minimum; setting to hard minimum'
Пример #8
0
def test_low_level_regrid1d_non_overlapping_not_allowed():
    """Integrated data space must not overlap"""

    c = Box1D()
    lo = np.linspace(1, 100, 600)
    hi = np.linspace(2, 101, 600)
    with pytest.raises(ModelErr) as excinfo:
        c.regrid(lo, hi)

    assert ModelErr.dict['needsint'] in str(excinfo.value)
Пример #9
0
def make_1d_model():
    """We want to create a new model each call.

    This could be made a fixture but this is easier.
    """

    box = Box1D('box1')
    box.xlow = 2
    box.xhi = 5
    return box
Пример #10
0
def test_low_level_regrid1d_non_overlapping_not_allowed():
    """Integrated data space must not overlap"""

    tmp = np.linspace(1, 100, 10)
    y = np.ones((9, ))
    d = Data1DInt('tst', tmp[:-1], tmp[1:], np.ones((9, )))
    c = Box1D()
    lo = np.linspace(1, 100, 600)
    hi = np.linspace(2, 101, 600)
    with pytest.raises(ModelErr) as excinfo:
        c.regrid(lo, hi)

    assert ModelErr.dict['needsint'] in str(excinfo.value)
Пример #11
0
def test_psf_set_model_to_model(kernel_func):
    """Can we change the model field? model

    This is a regression test.
    """

    m = PSFModel(kernel=kernel_func())
    with pytest.raises(AttributeError) as err:
        m.model = Box1D()

    assert str(
        err.value
    ) == "'PSFModel' object attribute 'model' cannot be replaced with a callable attribute"
Пример #12
0
def test_psf1d_no_fold():
    """Error out if there's no kernel"""

    box = Box1D()
    psf = PSFModel('bob', box)

    cpt = Gauss1D()
    sm = psf(cpt)

    with pytest.raises(PSFErr) as exc:
        sm([1, 2, 3, 4, 5])

    assert "PSF model has not been folded" == str(exc.value)
Пример #13
0
def test_psf1d_convolved_pars():
    """What does .pars mean for a PSFModel applied to a model?"""

    b1 = Box1D('b1')
    m = PSFModel(kernel=b1)
    b2 = Box1D('b2')
    c = m(b2)

    b1.xlow = 1
    b1.xhi = 10
    b1.ampl = 0.2

    b2.xlow = 4
    b2.xhi = 8
    b2.ampl = 0.4

    bpars = b1.pars + b2.pars
    assert len(bpars) == 6

    cpars = c.pars
    assert len(cpars) == 6
    for bpar, cpar in zip(bpars, cpars):
        assert cpar == bpar
Пример #14
0
def test_read_ideal_rmf():
    """Can a RMF similar to issue #862 be read in?

    The MATRIX column in this file is a scalar rather than array,
    and let's do EBOUNDS then MATRIX blocks.
    """

    from sherpa.astro.io import read_rmf

    ebins = np.arange(0.15, 0.2, 0.01)
    elo = ebins[:-1]
    ehi = ebins[1:]

    with NamedTemporaryFile() as f:
        fake_rmf(f.name)
        r = read_rmf(f.name)

    # Can we read in the data
    #
    assert r.detchans == 5
    assert r.energ_lo == pytest.approx(elo)
    assert r.energ_hi == pytest.approx(ehi)
    assert (r.n_grp == [1, 1, 1, 1, 1]).all()
    assert (r.f_chan == [1, 2, 3, 4, 5]).all()
    assert (r.n_chan == [1, 1, 1, 1, 1]).all()
    assert r.offset == 1
    assert r.e_min == pytest.approx(elo)
    assert r.e_max == pytest.approx(ehi)
    assert r.ethresh == 1e-10

    # Can we apply it?
    #
    # The cmdl evalutes to a value of 2 * bin width
    # The bmdl evalates to the bin width * x
    # where x = [0, 1, 0.5, 0, 0]
    #
    cmdl = Const1D()
    cmdl.c0 = 2

    bmdl = Box1D()
    bmdl.xlow = 0.16
    bmdl.xhi = 0.175

    mdl = bmdl + cmdl

    # Multiply by 100 so numbers are close to unity
    expected = 100 * 0.01 * np.asarray([2, 3, 2.5, 2, 2])
    y = 100 * r.eval_model(mdl)
    assert y == pytest.approx(expected, rel=2e-6)
Пример #15
0
def test_ui_regrid1d_non_overlapping_not_allowed():
    """Integrated data space must not overlap"""

    ui.dataspace1d(1, 100, 2, dstype=Data1DInt)
    b1 = Box1D()
    ui.set_model(b1)
    b1.xlow = 10
    b1.xhi = 80
    b1.ampl.max = 100
    grid_hi = np.linspace(2, 101, 600)
    grid_lo = np.linspace(1, 100, 600)
    with pytest.raises(ModelErr) as excinfo:
        rb1 = b1.regrid(grid_lo, grid_hi)

    assert ModelErr.dict['needsint'] in str(excinfo.value)
Пример #16
0
def test_low_level_regrid1d_full_overlap(requested):
    """Base case of test_low_level_regrid1d_partial_overlap
    """

    # The range over which we want the model evaluated
    xgrid = np.arange(2, 6, 0.1)
    d = Data1D('tst', xgrid, np.ones_like(xgrid))

    mdl = Box1D()
    mdl.xlow = 3.1
    mdl.xhi = 4.2
    mdl.ampl = 0.4

    yexpected = d.eval_model(mdl)
    assert yexpected.min() == pytest.approx(0.0)
    assert yexpected.max() == pytest.approx(0.4)

    ygot = d.eval_model(mdl.regrid(requested))
    assert ygot == pytest.approx(yexpected)
Пример #17
0
def test_low_level_regrid1d_int_full_overlap(requested, tol):
    """Base case of test_low_level_regrid1d_int_partial_overlap
    """

    # The range over which we want the model evaluated
    dx = 0.1
    xgrid = np.arange(2, 6, dx)
    xlo = xgrid[:-1]
    xhi = xgrid[1:]
    d = Data1DInt('tst', xlo, xhi, np.ones_like(xlo))

    mdl = Box1D()
    mdl.xlow = 3.1
    mdl.xhi = 4.2
    mdl.ampl = 0.4

    yexpected = d.eval_model(mdl)
    assert yexpected.min() == pytest.approx(0.0)
    assert yexpected.max() == pytest.approx(0.4 * dx)

    ygot = d.eval_model(mdl.regrid(requested[:-1], requested[1:]))
    assert ygot == pytest.approx(yexpected, abs=tol)
Пример #18
0
def test_psf1d_pars():
    """What does .pars mean for a PSFModel?"""

    b = Box1D()
    m = PSFModel(kernel=b)
    assert m.pars == ()
Пример #19
0
def test_calc_sherpa_regrid():
    """Test the CFLUX convolution model calculations (Sherpa model)

    Can we redshift a sherpa model and get the expected
    result, with a regrid applied? In this case a feature
    is added outside the default energy range, but in the
    regridded range, to check things are working.

    See Also
    --------
    test_calc_xspec_regrid

    """

    from sherpa.astro import xspec

    mdl = Box1D('box')
    mdl.xlow = 4
    mdl.xhi = 12
    # why is the box amplitude restricted like this?
    mdl.ampl.max = 2
    mdl.ampl = 2

    kern = xspec.XSzashift('zshift')
    kern.redshift = 1.0
    mdl_convolved = kern(mdl)

    d = setup_data(elo=0.1, ehi=10, ebin=0.01)
    dr = setup_data(elo=0.1, ehi=13, ebin=0.005)

    mdl_regrid = mdl_convolved.regrid(dr.xlo, dr.xhi)

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

    # We expect values < 2 keV to be 0
    #   yconvolved:  > 2 keV to < 5 keV to be 0.02 (ampl / bin width)
    #   yregrid:     > 2 keV to < 6 keV to be 0.02
    # above this to be 0, with some fun at the edges.
    #
    ehi = d.xhi

    idx = np.where(ehi < 2)
    ymax = np.abs(yconvolved[idx]).max()
    assert ymax == 0.0, 'yconvolved < 2 keV: max={}'.format(ymax)

    idx = np.where((ehi >= 2) & (ehi < 5))
    ymax = np.abs(yconvolved[idx] - 0.02).max()
    assert ymax < 1e-14, 'yconvolved: 2-5 keV max={}'.format(ymax)

    idx = np.where(ehi >= 5)
    ymax = np.abs(yconvolved[idx]).max()
    assert ymax == 0.0, 'yconvolved: > 5 keV max={}'.format(ymax)

    # expect last bin to be ~ 0 but preceeding ones to be zero
    idx = np.where(ehi < 2)
    ymax = np.abs(yregrid[idx][:-1]).max()
    assert ymax == 0, 'yregrid < 2 keV: max={}'.format(ymax)

    ymax = np.abs(yregrid[idx])[-1]
    assert ymax < 1e-14, 'yregrid < 2 keV: max={}'.format(ymax)

    idx = np.where((ehi >= 2) & (ehi < 6))
    ymax = np.abs(yregrid[idx] - 0.02).max()
    assert ymax < 2e-14, 'yregrid: 2-6 keV {}'.format(ymax)

    # expect first bin to be ~ 0 but following ones to be zero
    idx = np.where(ehi >= 6)
    ymax = np.abs(yregrid[idx])[0]
    assert ymax < 2e-14, 'yregrid: > 6 keV max={}'.format(ymax)

    ymax = np.abs(yregrid[idx][1:]).max()
    assert ymax == 0.0, 'yregrid: > 6 keV max={}'.format(ymax)
Пример #20
0
def test_low_level_regrid1d_int_partial_overlap(requested, tol):
    """What happens if there is partial overlap of the grid?

    See test_low_level_regrid1d_partial_overlap.

    See the comments for when the tol value is used (only for
    edges, not "flat" sections of the model).
    """

    # The range over which we want the model evaluated
    dx = 0.1
    xgrid = np.arange(2, 6, dx)
    xlo = xgrid[:-1]
    xhi = xgrid[1:]
    d = Data1DInt('tst', xlo, xhi, np.ones_like(xlo))

    mdl = Box1D()
    mdl.xlow = 3.1
    mdl.xhi = 4.2
    mdl.ampl = 0.4

    yexpected = d.eval_model(mdl)
    assert yexpected.min() == pytest.approx(0.0)
    assert yexpected.max() == pytest.approx(0.4 * dx)

    rlo = requested[:-1]
    rhi = requested[1:]
    ygot = d.eval_model(mdl.regrid(rlo, rhi))

    # Note that due to the different bin widths of the input and
    # output bins, the start and end bins of the integrated box
    # model will not line up nicely, and so the rising and falling
    # edges may cause differences for more than a single bin. We
    # can think of there being five regions (going from left to
    # right along the grid):
    #
    #    before
    #    rising edge
    #    middle of box
    #    falling edge
    #    after
    #
    # The expected bin values for the middle are 0.4 * dx = 0.04
    # and before and after should be 0. The edges depend on the
    # bin widths, so the tolerance here had been adjusted until
    # the tests pass.
    #
    before = np.arange(0, 10)
    rising = np.arange(10, 12)
    middle = np.arange(12, 21)
    trailing = np.arange(21, 23)
    after = np.arange(23, 39)

    # This ensures that if xgrid is changed (in length at least) we
    # have a reminder to check the above ranges.
    #
    assert len(xlo) == 39

    for idxs in [before, middle, after]:
        assert ygot[idxs] == pytest.approx(yexpected[idxs])

    for idxs in [rising, trailing]:
        assert ygot[idxs] == pytest.approx(yexpected[idxs], abs=tol)
Пример #21
0
def test_cache_clear_multiple(caplog):
    """Check cache_clear for a combined model."""

    p = Polynom1D()
    b = Box1D()
    c = Const1D()
    mdl = c * (p + 2 * b)

    # Ensure one component doesn't use the cache
    c._use_caching = False

    # There's no official API for accessing the cache data,
    # so do it directly.
    #
    assert len(p._cache) == 0
    assert p._cache_ctr['check'] == 0
    assert p._cache_ctr['hits'] == 0
    assert p._cache_ctr['misses'] == 0

    assert len(b._cache) == 0
    assert b._cache_ctr['check'] == 0
    assert b._cache_ctr['hits'] == 0
    assert b._cache_ctr['misses'] == 0

    assert len(c._cache) == 0
    assert c._cache_ctr['check'] == 0
    assert c._cache_ctr['hits'] == 0
    assert c._cache_ctr['misses'] == 0

    mdl([1, 2, 3])
    mdl([1, 2, 3])
    mdl([1, 2, 3, 4])

    assert len(p._cache) == 1
    assert p._cache_ctr['check'] == 3
    assert p._cache_ctr['hits'] == 1
    assert p._cache_ctr['misses'] == 2

    assert len(b._cache) == 1
    assert b._cache_ctr['check'] == 3
    assert b._cache_ctr['hits'] == 1
    assert b._cache_ctr['misses'] == 2

    assert len(c._cache) == 0
    assert c._cache_ctr['check'] == 3
    assert c._cache_ctr['hits'] == 0
    assert c._cache_ctr['misses'] == 0

    mdl.cache_clear()

    assert len(p._cache) == 0
    assert p._cache_ctr['check'] == 0
    assert p._cache_ctr['hits'] == 0
    assert p._cache_ctr['misses'] == 0

    assert len(b._cache) == 0
    assert b._cache_ctr['check'] == 0
    assert b._cache_ctr['hits'] == 0
    assert b._cache_ctr['misses'] == 0

    assert len(c._cache) == 0
    assert c._cache_ctr['check'] == 0
    assert c._cache_ctr['hits'] == 0
    assert c._cache_ctr['misses'] == 0