コード例 #1
0
def test_delete_bkg_model(clean_astro_ui):
    """Check we can delete a background model"""

    channels = np.arange(1, 5)
    counts = np.zeros(channels.size)
    d = ui.DataPHA('src', channels, counts)
    b = ui.DataPHA('bkg', channels, counts)
    d.set_background(b, id=2)
    ui.set_data(d)

    ui.set_bkg_source(ui.const1d.bmdl + ui.gauss1d.gmdl, bkg_id=2)
    assert ui.get_bkg_source(bkg_id=2) is not None

    ui.delete_bkg_model(bkg_id=2)

    # Expression has been removed
    #
    with pytest.raises(ModelErr) as exc:
        ui.get_bkg_source(bkg_id=2)

    assert str(
        exc.value) == 'background model 2 for data set 1 has not been set'

    # components still exist
    mdls = ui.list_model_components()
    assert set(mdls) == set(['bmdl', 'gmdl'])
コード例 #2
0
ファイル: test_astro_ui_plot.py プロジェクト: JBris/sherpa
def example_pha_data():
    """Create an example data set."""

    etime = 1201.0
    d = ui.DataPHA('example',
                   _data_chan.copy(),
                   _data_counts.copy(),
                   exposure=etime,
                   backscal=0.1)

    a = ui.create_arf(_energies[:-1].copy(),
                      _energies[1:].copy(),
                      specresp=_arf.copy(),
                      exposure=etime)

    r = ui.create_rmf(_energies[:-1].copy(),
                      _energies[1:].copy(),
                      e_min=_energies[:-1].copy(),
                      e_max=_energies[1:].copy(),
                      startchan=1,
                      fname=None)

    d.set_arf(a)
    d.set_rmf(r)
    return d
コード例 #3
0
def test_default_background_issue_fit(clean_astro_ui):
    """Test issue #943 with fit

    See https://github.com/sherpa/sherpa/issues/943#issuecomment-696119982
    """

    ui.set_default_id('x')

    # use least-square as we don't really care about the fit
    ui.set_stat('leastsq')

    ui.load_arrays('x', [1, 2, 3, 4], [5, 4, 3, 4], ui.DataPHA)
    bkg = ui.DataPHA('bkg', np.asarray([1, 2, 3, 4]), [1, 1, 0, 1])
    arf = ui.create_arf(np.asarray([0.1, 0.2, 0.3, 0.4]),
                        np.asarray([0.2, 0.3, 0.4, 0.5]))
    ui.set_arf(arf)
    bkg.set_arf(arf)
    ui.set_bkg(bkg)

    # The model being fitted is a constant to 1,1,0,1 for
    # the background, so that should be 0.75 / 0.1 (as the
    # bin width is constant), and for the source it is
    # 5,4,3,4 - <0.75> [here ignoring the bin-width],
    # so [4.25,3.25,2.25,3.25] -> 13 / 4 -> 3.25
    #
    ui.set_source(ui.const1d.mdl1)
    ui.set_bkg_source(ui.const1d.mdl2)

    # Prior to #943 this would give a confusing error.
    #
    ui.fit()
    assert mdl1.c0.val == pytest.approx(3.25 / 0.1)
    assert mdl2.c0.val == pytest.approx(0.75 / 0.1)
コード例 #4
0
def test_fake_pha_background_model(clean_astro_ui, reset_seed):
    """Check we can add a background component.

    See also test_fake_pha_basic.

    For simplicity we use perfect responses.
    """

    np.random.seed(27347)

    id = 'qwerty'
    channels = np.arange(1, 4, dtype=np.int16)
    counts = np.ones(3, dtype=np.int16)
    bcounts = 100 * counts

    ui.load_arrays(id, channels, counts, ui.DataPHA)
    ui.set_exposure(id, 100)
    ui.set_backscal(id, 0.1)

    bkg = ui.DataPHA('bkg', channels, bcounts, exposure=200, backscal=0.4)

    ebins = np.asarray([1.1, 1.2, 1.4, 1.6])
    elo = ebins[:-1]
    ehi = ebins[1:]
    arf = ui.create_arf(elo, ehi)
    rmf = ui.create_rmf(elo, ehi, e_min=elo, e_max=ehi)

    mdl = ui.create_model_component('const1d', 'mdl')
    mdl.c0 = 0
    bkgmdl = ui.create_model_component('const1d', 'mdl')
    bkgmdl.c0 = 2
    ui.set_source(id, mdl)
    ui.set_bkg(id, bkg)
    ui.set_bkg_source(id, bkgmdl)
    ui.set_arf(id, arf, bkg_id=1)
    ui.set_rmf(id, rmf, bkg_id=1)

    ui.fake_pha(id, arf, rmf, 1000.0, bkg='model')

    faked = ui.get_data(id)
    assert faked.exposure == pytest.approx(1000.0)
    assert (faked.channel == channels).all()

    # check we've faked counts (the scaling is such that it is
    # very improbable that this condition will fail)
    assert (faked.counts > counts).all()

    # For reference the predicted source signal is
    #    [200, 400, 400]
    # and the background signal is
    #    [125, 125, 125]
    # so, even with randomly drawn values, the following
    # checks should be robust.
    #
    predicted_by_source = 1000 * mdl(elo, ehi)
    predicted_by_bkg = (1000 / 200) * (0.1 / 0.4) * bcounts
    assert (faked.counts > predicted_by_source).all()
    assert (faked.counts > predicted_by_bkg).all()
コード例 #5
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)
コード例 #6
0
def test_get_syserror_missing(bid):
    """Does get_syserror error out?"""

    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)
    with pytest.raises(DataErr) as exc:
        ui.get_syserror(bkg_id=bid)

    assert str(exc.value) == "data set '1' does not specify systematic errors"
コード例 #7
0
def test_set_filter_mismatch(f, bid):
    """Does set_filter error when there's a mis-match?
    """

    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)
    with pytest.raises(DataErr) as exc:
        ui.set_filter(f, bkg_id=bid)

    assert str(exc.value) == 'size mismatch between 3 and 2'
コード例 #8
0
def test_fix_background_id_error_checks2():
    """Check error handling of background id"""

    ui.load_arrays(2, [1, 2, 3], [5, 4, 3], ui.DataPHA)
    bkg = ui.DataPHA('bkg', np.asarray([1, 2, 3]), [1, 1, 0])
    ui.set_bkg(2, bkg)

    with pytest.raises(IdentifierErr) as exc:
        ui.get_bkg_source(id=2, bkg_id='bkg')

    assert str(exc.value) == "identifier 'bkg' is a reserved word"
コード例 #9
0
def test_fix_background_id_error_checks1():
    """Check error handling of background id"""

    ui.load_arrays(2, [1, 2, 3], [5, 4, 3], ui.DataPHA)
    bkg = ui.DataPHA('bkg', np.asarray([1, 2, 3]), [1, 1, 0])
    ui.set_bkg(2, bkg)

    with pytest.raises(ArgumentTypeErr) as exc:
        ui.get_bkg_source(id=2, bkg_id=bkg)

    assert str(exc.value) == 'identifiers must be integers or strings'
コード例 #10
0
def test_save_xxx_nodata(func, etype, emsg):
    """Does save_xxx error out if there's no data to save? DataPHA
    """

    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)

    with pytest.raises(etype) as exc:
        func("temp-file-that-should-not-be-created")

    assert str(exc.value) == emsg
コード例 #11
0
def get_egrid():
    """The standard energy grid / dataset for the tests."""

    egrid = np.arange(0.1, 10, 0.01)

    nchans = egrid.size - 1
    assert nchans == 989

    chans = np.arange(1, nchans + 1)
    y = np.zeros_like(chans)
    dset = ui.DataPHA('faked', chans, y)

    return egrid[:-1].copy(), egrid[1:].copy(), dset
コード例 #12
0
def test_save_image_nodata():
    """Does save_image error out if there's no data to save? DataPHA

    Unlike the other calls, this requires a FITS library
    """

    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)

    with pytest.raises(IOErr) as exc:
        ui.save_image("temp-file-that-should-not-be-created")

    assert str(exc.value) == "data set '' does not contain an image"
コード例 #13
0
def test_bkg_id_get_bkg_source(clean_astro_ui):
    """Check the error message when the background model has not been set (issue #943)"""

    ui.set_default_id('x')

    ui.load_arrays('x', [1, 2, 3], [5, 4, 3], ui.DataPHA)
    bkg = ui.DataPHA('bkg', np.asarray([1, 2, 3]), [1, 1, 0])
    ui.set_bkg(bkg)

    with pytest.raises(ModelErr) as exc:
        ui.get_bkg_source()

    assert str(
        exc.value) == 'background model 1 for data set x has not been set'
コード例 #14
0
def test_save_xxx_bkg_nodata(func, etype, emsg):
    """Does save_xxx error out if there's no data to save? DataPHA + bkg

    Note that save_image does not support a bkg_id parameter so is
    not tested here.
    """

    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)

    with pytest.raises(etype) as exc:
        func("temp-file-that-should-not-be-created", bkg_id=1)

    assert str(exc.value) == emsg
コード例 #15
0
def example_pha_with_bkg_data():
    """Create an example data set with background

    There is no response for the background.
    """

    d = example_pha_data()

    b = ui.DataPHA('example-bkg', _data_chan.copy(),
                   _data_bkg.copy(),
                   exposure=1201.0 * _bexpscale,
                   backscal=0.4)

    d.set_background(b)
    return d
コード例 #16
0
def test_grouped_pha_all_bad_response(arf, rmf, chantype, exp_counts, exp_xlo,
                                      exp_xhi, lo1, hi1, lo2, hi2,
                                      clean_astro_ui):
    """Helpdesk ticket: low-count data had no valid bins after grouping #790

    A simple PHA dataset is created, which has no "good" grouped data
    (1 group, but with a quality of 2). Several checks are made to
    ensure we can filter/notice/plot the data even when it is empty.

    Checks are done for
      - arf-only
      - rmf-only
      - arf+rmf
    analysis in case there's a difference in the code paths

    """

    chans = numpy.arange(1, 6, dtype=numpy.int16)
    counts = numpy.asarray([0, 1, 2, 0, 1], dtype=numpy.int16)
    grouping = numpy.asarray([1, -1, -1, -1, -1], dtype=numpy.int16)
    quality = numpy.asarray([2, 2, 2, 2, 2], dtype=numpy.int16)

    dset = ui.DataPHA('low', chans, counts, grouping=grouping, quality=quality)
    ui.set_data(dset)

    egrid = numpy.asarray([0.1, 0.2, 0.3, 0.4, 0.5, 0.6])
    elo = egrid[:-1]
    ehi = egrid[1:]

    # it is required that at least one of arf or rmf is set but this
    # is not enforced
    #
    if arf:
        ui.set_arf(ui.create_arf(elo, ehi))

    if rmf:
        # NOTE: need to set e_min/max otherwise get a 'noenergybins'
        #       error from sherpa.astro.data.DataPHA._get_ebins
        #
        ui.set_rmf(ui.create_rmf(elo, ehi, e_min=elo, e_max=ehi))

    # plot units depend on analysis type;
    #
    ui.set_analysis(chantype)

    # Run tests
    check_bad_grouping(exp_xlo, exp_xhi, exp_counts, lo1, hi1, lo2, hi2)
コード例 #17
0
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"
コード例 #18
0
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'
コード例 #19
0
    def test_cache_copy(self):
        # fake up a PHA data set
        chans = numpy.arange(1, 11, dtype=numpy.int8)
        counts = numpy.ones(chans.size)

        # bin width is not 0.1 but something slightly different
        ebins = numpy.linspace(0.1, 1.2, num=chans.size + 1)
        elo = ebins[:-1]
        ehi = ebins[1:]

        dset = ui.DataPHA('test', chans, counts)

        # make sure ARF isn't 1 to make sure it's being applied
        arf = ui.create_arf(elo, ehi, specresp=0.7 * numpy.ones(chans.size))

        rmf = ui.create_rmf(elo, ehi, e_min=elo, e_max=ehi)

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

        ui.set_source(ui.const1d.mdl)

        # again not 1
        mdl.c0 = 8


        # Copy the values from the plot structures, since get_xxx_plot
        # returns the same object so m1.y == m2.y will not note a difference.
        #

        d1y = ui.get_data_plot().y.copy()
        m1y = ui.get_model_plot().y.copy()
        s1y = ui.get_source_plot().y.copy()

        d2y = ui.get_data_plot().y.copy()
        m2y = ui.get_model_plot().y.copy()
        s2y = ui.get_source_plot().y.copy()
        rtol = 1.0e-4
        atol = 1.0e-4
        numpy.testing.assert_allclose(d1y, d2y, rtol, atol)
        numpy.testing.assert_allclose(m1y, m2y, rtol, atol)
        numpy.testing.assert_allclose(s1y, s2y, rtol, atol)
コード例 #20
0
def test_delete_bkg_model_with_bkgid(id, clean_astro_ui):
    """Check we call delete_bkg_model with non-default bkg_id"""

    ui.load_arrays(id, [1, 2, 3], [5, 4, 3], ui.DataPHA)
    bkg = ui.DataPHA('bkg', np.asarray([1, 2, 3]), [1, 1, 0])
    ui.set_bkg(id, bkg, bkg_id=2)

    ui.set_bkg_source(id, ui.const1d.bmdl, bkg_id=2)
    assert ui.list_model_components() == ['bmdl']
    assert ui.get_bkg_source(id, 2).name == 'const1d.bmdl'

    ui.delete_bkg_model(id, bkg_id=2)
    assert ui.list_model_components() == ['bmdl']

    with pytest.raises(ModelErr) as exc:
        ui.get_bkg_source(id, 2)

    emsg = 'background model 2 for data set {} has not been set'.format(id)
    assert str(exc.value) == emsg
コード例 #21
0
def test_show_bkg_model_issue943(clean_astro_ui):
    """Test issue #943

    We do not check that show_bkg_model is creating anything
    useful, just that it can be called.

    See https://github.com/sherpa/sherpa/issues/943#issuecomment-696119982
    """

    ui.set_default_id('x')

    ui.load_arrays('x', [1, 2, 3], [5, 4, 3], ui.DataPHA)
    bkg = ui.DataPHA('bkg', np.asarray([1, 2, 3]), [1, 1, 0])
    arf = ui.create_arf(np.asarray([0.1, 0.2, 0.3]),
                        np.asarray([0.2, 0.3, 0.4]))
    bkg.set_arf(arf)
    ui.set_bkg(bkg)

    ui.set_bkg_source(ui.const1d.mdl2)
    ui.show_bkg_model()
コード例 #22
0
def test_grouped_pha_all_bad_channel(clean_astro_ui):
    """Helpdesk ticket: low-count data had no valid bins after grouping #790

    A simple PHA dataset is created, with no response, which has no
    "good" grouped data (1 group, but with a quality of 2). Several
    checks are made to ensure we can filter/notice/plot the data even
    when it is empty.  This is in channel space.

    See also test_grouped_pha_all_bad_response which is essentially the
    same test but with a response model.

    """
    chans = numpy.arange(1, 6, dtype=numpy.int16)
    counts = numpy.asarray([0, 1, 2, 0, 1], dtype=numpy.int16)
    grouping = numpy.asarray([1, -1, -1, -1, -1], dtype=numpy.int16)
    quality = numpy.asarray([2, 2, 2, 2, 2], dtype=numpy.int16)

    dset = ui.DataPHA('low', chans, counts, grouping=grouping, quality=quality)
    ui.set_data(dset)

    # Run tests
    check_bad_grouping(1, 6, 0.8, 0, 6, 2, 10)
コード例 #23
0
def test_default_background_issue(clean_astro_ui):
    """Test issue #943"""

    ui.set_default_id('x')

    # use least-square as we don't really care about the fit
    ui.set_stat('leastsq')

    ui.load_arrays('x', [1, 2, 3], [5, 4, 3], ui.DataPHA)
    bkg = ui.DataPHA('bkg', np.asarray([1, 2, 3]), [1, 1, 0])
    arf = ui.create_arf(np.asarray([0.1, 0.2, 0.3]),
                        np.asarray([0.2, 0.3, 0.4]))
    bkg.set_arf(arf)
    ui.set_bkg(bkg)

    ui.set_bkg_source(ui.const1d.mdl2)

    # Ensure we can fit the background model. Prior to #943 being
    # fixed the fit_bkg call would error out.
    #
    ui.fit_bkg()
    assert mdl2.c0.val == pytest.approx(2 / 3 / 0.1)
コード例 #24
0
def test_fake_pha_basic(id, has_bkg, clean_astro_ui):
    """No background.

    See also test_fake_pha_add_background

    For simplicity we use perfect responses.

    A background dataset can be added, but it should
    not be used in the simulation.
    """

    channels = np.arange(1, 4, dtype=np.int16)
    counts = np.ones(3, dtype=np.int16)
    bcounts = 100 * counts

    ui.load_arrays(id, channels, counts, ui.DataPHA)
    ui.set_exposure(id, 100)

    if has_bkg:
        bkg = ui.DataPHA('bkg', channels, bcounts, exposure=200, backscal=0.4)
        ui.set_bkg(id, bkg, bkg_id='faked-bkg')

    ebins = np.asarray([1.1, 1.2, 1.4, 1.6])
    elo = ebins[:-1]
    ehi = ebins[1:]
    arf = ui.create_arf(elo, ehi)
    rmf = ui.create_rmf(elo, ehi, e_min=elo, e_max=ehi)

    mdl = ui.create_model_component('const1d', 'mdl')
    mdl.c0 = 2
    ui.set_source(id, mdl)

    ui.fake_pha(id, arf, rmf, 1000.0)

    faked = ui.get_data(id)
    assert faked.exposure == pytest.approx(1000.0)
    assert (faked.channel == channels).all()

    assert faked.name == 'faked'
    assert faked.get_arf().name == 'test-arf'
    assert faked.get_rmf().name == 'delta-rmf'

    if has_bkg and id is not None:
        assert faked.background_ids == ['faked-bkg']
        bkg = ui.get_bkg(id, 'faked-bkg')
        assert bkg.name == 'bkg'
        assert bkg.counts == pytest.approx(bcounts)
        assert bkg.exposure == pytest.approx(200)

    else:
        assert faked.background_ids == []

    # check we've faked counts (the scaling is such that it is
    # very improbable that this condition will fail)
    assert (faked.counts > counts).all()

    # For reference the predicted source signal is
    #    [200, 400, 400]
    #
    # What we'd like to say is that the predicted counts are
    # similar, but this is not easy to do. What we can try
    # is summing the counts (to average over the randomness)
    # and then a simple check
    #
    assert faked.counts.sum() > 200
コード例 #25
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)