def _fit_sherpa(self): """Wrapper around sherpa minimizer.""" from sherpa.fit import Fit from sherpa.data import Data1DInt from sherpa.optmethods import NelderMead from .sherpa_utils import SherpaModel, SherpaStat binning = self.obs_list[0].e_reco # The sherpa data object is not usued in the fit. It is set to the # first observation for debugging purposes, see below data = self.obs_list[0].on_vector.data.data.value data = Data1DInt('Dummy data', binning[:-1].value, binning[1:].value, data) # DEBUG # from sherpa.models import PowLaw1D # from sherpa.stats import Cash # model = PowLaw1D('sherpa') # model.ref = 0.1 # fit = Fit(data, model, Cash(), NelderMead()) # NOTE: We cannot use the Levenbergr-Marquart optimizer in Sherpa # because it relies on the fvec return value of the fit statistic (we # return None). The computation of fvec is not straightforwad, not just # stats per bin. E.g. for a cash fit the sherpa stat computes it # according to cstat # see https://github.com/sherpa/sherpa/blob/master/sherpa/include/sherpa/stats.hh#L122 self._sherpa_fit = Fit(data, SherpaModel(self), SherpaStat(self), NelderMead()) fitresult = self._sherpa_fit.fit() log.debug(fitresult) self._make_fit_result(self.model.parameters)
def setup_data(elo=0.1, ehi=10.0, ebin=0.01): """Return a data set. Parameters ---------- elo, ehi : number, optional The start and end energy of the grid, in keV. ebin : number, optional The bin width, in keV. Returns ------- data : Data1DInt The data object. The Y values are not expected to be used, so are set to 1. """ if elo >= ehi: raise ValueError("elo >= ehi") if elo <= 0: raise ValueError("elo <= 0") if ebin <= 0: raise ValueError("ebin <= 0") x = np.arange(elo, ehi + ebin, ebin) if x.size < 2: raise ValueError("elo, ehi, ebin not sensible") y = np.ones(x.size - 1) return Data1DInt('dummy', x[:-1], x[1:], y)
def make_data(data_class): """Create a test data object of the given class. Using a string means it is easier to support the various PHA "types" - eg basic, grouping, grouping+quality. """ x0 = np.asarray([1, 3, 7, 12]) y = np.asarray([2, 3, 4, 5]) if data_class == "1d": return Data1D('x1', x0, y) if data_class == "1dint": return Data1DInt('xint1', x0, np.asarray([3, 5, 8, 15]), y) chans = np.arange(1, 5) if data_class == "pha": return DataPHA('pha', chans, y) # We want to provide PHA tests that check out the grouping and # quality handling (but it is not worth trying all different # variants), so we have "grp" for grouping and no quality [*], and # "qual" for grouping and quality. # # [*] by which I mean we have not called ignore_bad, not that # there is no quality array. # grp = np.asarray([1, -1, 1, 1]) qual = np.asarray([0, 0, 2, 0]) pha = DataPHA('pha', chans, y, grouping=grp, quality=qual) if data_class == "grp": return pha if data_class == "qual": pha.ignore_bad() return pha x0 = np.asarray([1, 2, 3] * 2) x1 = np.asarray([1, 1, 1, 2, 2, 2]) y = np.asarray([2, 3, 4, 5, 6, 7]) if data_class == "2d": return Data2D('x2', x0, x1, y, shape=(2, 3)) if data_class == "2dint": return Data2DInt('xint2', x0, x1, x0 + 1, x1 + 1, y, shape=(2, 3)) if data_class == "img": return DataIMG('img', x0, x1, y, shape=(2, 3)) if data_class == "imgint": return DataIMGInt('imgi', x0, x1, x0 + 1, x1 + 1, y, shape=(2, 3)) assert False
def test_histogram_can_not_set_x(): """We cannot change the x accessor""" xlo = [10, 20, 40, 80] xhi = [20, 40, 60, 90] y = [1, 2, 3, 4] d = Data1DInt('xx', xlo, xhi, y) dp = sherpaplot.DataHistogramPlot() dp.prepare(d) with pytest.raises(AttributeError): dp.x = xlo
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)
def test_warning_plot_hist_linecolor(caplog): """We get a warning when using linecolor: ModelHistogramPlot""" data = Data1DInt('tst', np.asarray([1, 2, 3]), np.array([2, 2.5, 4]), np.asarray([10, 12, 10.5])) mdl = Const1D() plot = ModelHistogramPlot() plot.prepare(data, mdl, stat=None) with caplog.at_level(logging.INFO, logger='sherpa'): plot.plot(linecolor='mousey') assert len(caplog.records) == 1 lname, lvl, msg = caplog.record_tuples[0] assert lname == 'sherpa.plot.pylab_backend' assert lvl == logging.WARNING assert msg == 'The linecolor attribute (mousey) is unused.'
def setup_single_1dint(stat, sys): """Return a single data set and model. Parameters ---------- stat, sys : bool Should statistical and systematic errors be explicitly set (True) or taken from the statistic (False)? Returns ------- data, model Data1DInt and Model objects. """ xlo = np.asarray([-10, -5, 3, 4]) xhi = np.asarray([-5, 2, 4, 7]) y = np.asarray([16.3, 2.4, 4.3, 5.6]) if stat: # pick values close to sqrt(y) staterr = np.asarray([4.0, 1.6, 2.1, 2.4]) else: staterr = None if sys: syserr = 0.05 * y else: syserr = None data = Data1DInt('tst1', xlo, xhi, y, staterror=staterr, syserror=syserr) # As the model is integrated, the normalization values need to # be divided by the bin width, but the bins are not constant # width, so pick a value which gives reasonable values. # mdl = Polynom1D('mdl1') mdl.c0 = 0 mdl.c2 = 0.08 mdl.offset = -1 mdl.offset.frozen = False mdl.c2.frozen = False return data, mdl
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)
def test_histogram_returns_x(): """We support x accessor for histogram plots.""" xlo = [10, 20, 40, 80] xhi = [20, 40, 60, 90] y = [1, 2, 3, 4] d = Data1DInt('xx', xlo, xhi, y) dp = sherpaplot.DataHistogramPlot() dp.prepare(d) xlo = numpy.asarray(xlo) xhi = numpy.asarray(xhi) assert dp.xlo == pytest.approx(xlo) assert dp.xhi == pytest.approx(xhi) assert dp.x == pytest.approx(0.5 * (xlo + xhi)) # check x is not in the str output # for line in str(dp).split('\n'): toks = line.split() assert len(toks) > 2 assert toks[0] != 'x'
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)
def _make_dataset(n_dim, x, y, z=None, xbinsize=None, ybinsize=None, err=None, bkg=None, bkg_scale=1, n=0): """ Parameters ---------- n_dim: int Used to veirfy required number of dimentions. x : array input coordinates y : array input coordinates z : array (optional) input coordinatesbkg xbinsize : array (optional) an array of errors in x ybinsize : array (optional) an array of errors in y err : array (optional) an array of errors in z n : int used in error reporting Returns ------- _data: a sherpa dataset """ if (z is None and n_dim > 1) or (z is not None and n_dim == 1): raise ValueError("Model and data dimentions don't match in dataset %i" % n) if z is None: assert x.shape == y.shape, "shape of x and y don't match in dataset %i" % n else: z = np.asarray(z) assert x.shape == y.shape == z.shape, "shapes x,y and z don't match in dataset %i" % n if xbinsize is not None: xbinsize = np.array(xbinsize) assert x.shape == xbinsize.shape, "x's and xbinsize's shapes do not match in dataset %i" % n if z is not None and err is not None: err = np.array(err) assert z.shape == err.shape, "z's and err's shapes do not match in dataset %i" % n if ybinsize is not None: ybinsize = np.array(ybinsize) assert y.shape == ybinsize.shape, "y's and ybinsize's shapes do not match in dataset %i" % n else: if err is not None: err = np.array(err) assert y.shape == err.shape, "y's and err's shapes do not match in dataset %i" % n if xbinsize is not None: bs = xbinsize / 2.0 if z is None: if xbinsize is None: if err is None: if bkg is None: data = Data1D("wrapped_data", x=x, y=y) else: data = Data1DBkg("wrapped_data", x=x, y=y, bkg=bkg, bkg_scale=bkg_scale) else: if bkg is None: data = Data1D("wrapped_data", x=x, y=y, staterror=err) else: data = Data1DBkg("wrapped_data", x=x, y=y, staterror=err, bkg=bkg, bkg_scale=bkg_scale) else: if err is None: if bkg is None: data = Data1DInt("wrapped_data", xlo=x - bs, xhi=x + bs, y=y) else: data = Data1DIntBkg("wrapped_data", xlo=x - bs, xhi=x + bs, y=y, bkg=bkg, bkg_scale=bkg_scale) else: if bkg is None: data = Data1DInt("wrapped_data", xlo=x - bs, xhi=x + bs, y=y, staterror=err) else: data = Data1DIntBkg("wrapped_data", xlo=x - bs, xhi=x + bs, y=y, staterror=err, bkg=bkg, bkg_scale=bkg_scale) else: if xbinsize is None and ybinsize is None: if err is None: if bkg is None: data = Data2D("wrapped_data", x0=x, x1=y, y=z) else: data = Data2DBkg("wrapped_data", x0=x, x1=y, y=z, bkg=bkg, bkg_scale=bkg_scale) else: if bkg is None: data = Data2D("wrapped_data", x0=x, x1=y, y=z, staterror=err) else: data = Data2DBkg("wrapped_data", x0=x, x1=y, y=z, staterror=err, bkg=bkg, bkg_scale=bkg_scale) elif xbinsize is not None and ybinsize is not None: ys = ybinsize / 2.0 if err is None: if bkg is None: data = Data2DInt("wrapped_data", x0lo=x - bs, x0hi=x + bs, x1lo=y - ys, x1hi=y + ys, y=z) else: data = Data2DIntBkg("wrapped_data", x0lo=x - bs, x0hi=x + bs, x1lo=y - ys, x1hi=y + ys, y=z, bkg=bkg, bkg_scale=bkg_scale) else: if bkg is None: data = Data2DInt("wrapped_data", x0lo=x - bs, x0hi=x + bs, x1lo=y - ys, x1hi=y + ys, y=z, staterror=err) else: data = Data2DIntBkg("wrapped_data", x0lo=x - bs, x0hi=x + bs, x1lo=y - ys, x1hi=y + ys, y=z, staterror=err, bkg=bkg, bkg_scale=bkg_scale) else: raise ValueError("Set xbinsize and ybinsize, or set neither!") return data
print(eval(name)) print("----------------------------------------") def dump(name): print("# dump") print("{}".format(name)) print(repr(eval(name))) print("----------------------------------------") edges = np.asarray([-10, -5, 5, 12, 17, 20, 30, 56, 60]) y = np.asarray([28, 62, 17, 4, 2, 4, 125, 55]) from sherpa.data import Data1DInt d = Data1DInt('example histogram', edges[:-1], edges[1:], y) from sherpa.plot import DataPlot dplot = DataPlot() dplot.prepare(d) dplot.plot() savefig('dataplot_histogram.png') from sherpa.plot import Histogram hplot = Histogram() hplot.overplot(d.xlo, d.xhi, d.y) savefig('dataplot_histogram_overplot.png') from sherpa.models.basic import Const1D, Gauss1D