def test_regrid1d_identity_when_no_grid_int(setup_1d): internal_mdl = setup_1d rmdl = ModelDomainRegridder1D() mdl = rmdl.apply_to(internal_mdl) # Ensure that the grid widths are not the same, # and that it is not contiguous. Would probably # have been easier to list the bins explicitly rather # than the following selection of filters. # grid = np.arange(-10, 100, 5) def excl(vlo, vhi): return (grid < vlo) | (grid > vhi) idx = excl(-1, 1) & excl(79, 86) grid = grid[idx] xlo = grid[:-1] xhi = grid[1:] idx = np.arange(xlo.size) != 4 xlo = xlo[idx] xhi = xhi[idx] yexp = internal_mdl(xlo, xhi) ygot = mdl(xlo, xhi) assert_allclose(ygot, yexp, atol=0, rtol=1e-7)
def test_regrid1d_wrapping_str(): """Check the str output of a wrapped model. Since this includes the name field, it subsumes test_regrid1d_wrapping_name but leave that test alone as it is an explicit check. """ # This is basically checking that __str__ comes from # the CompositeModel and that everything is set up # correctly. # internal_model = Const1D('con') internal_model.c0 = 2 internal_model.c0.freeze() imodel_name = internal_model.name rmdl = ModelDomainRegridder1D(name='test') mdl = rmdl.apply_to(internal_model) expected_name = 'test({})'.format(imodel_name) # need to strip off the first line and replace it with # the new model name expected_params = "\n".join(str(internal_model).split('\n')[1:]) expected_str = expected_name + "\n" + expected_params assert str(mdl) == expected_str
def test_regrid1d_passes_through_the_grid(): """Is the grid actually being passed through to the model?""" rmdl = ModelDomainRegridder1D() imdl = MyConst1D() imdl.c0 = -34.5 mdl = rmdl.apply_to(imdl) grid_expected = [5, 10, 15, 20, 25, 30] grid_requested = [12, 18, 20] rmdl.grid = grid_expected store = imdl._calc_store len(store) == 0 y = mdl(grid_requested) assert len(y) == len(grid_requested) assert len(store) == 1 store = store[0] assert len(store) == 2 assert len(store[0]) == 2 assert store[1] == {'integrate': True} store = store[0] assert store[0] == [-34.5] combine = np.unique(np.append(grid_expected, grid_requested)) indices = combine.searchsorted(store[1]) assert (store[1] == combine[indices]).all()
def test_interp_method_is_callable(): """Check that method is callable.""" rmdl = ModelDomainRegridder1D() with pytest.raises(TypeError) as exc: rmdl.method = True assert str(exc.value) == "method argument 'True' is not callable"
def test_regrid1d_wrapping_create_composite_instance(): # This test depends on what we want the regridded model to look like, which is # somewhat arbitrary cmdl = Const1D() gmdl = Gauss1D() imdl = cmdl + gmdl rmdl = ModelDomainRegridder1D() mdl = rmdl.apply_to(imdl) assert isinstance(mdl, CompositeModel) assert len(mdl.parts) == 1 assert mdl.parts[0] is imdl
def test_regrid1d_identity_when_no_grid_rev(setup_1d): internal_mdl = setup_1d rmdl = ModelDomainRegridder1D() mdl = rmdl.apply_to(internal_mdl) grid = np.arange(-10, 100, 5)[::-1] yexp = internal_mdl(grid) ygot = mdl(grid) assert_allclose(ygot, yexp, atol=0, rtol=1e-7)
def test_regrid1d_error_calc_no_args(setup_1d): internal_mdl = setup_1d grid_evaluate = EvaluationSpace1D(np.arange(-10, 100, 5)) rmdl = ModelDomainRegridder1D(grid_evaluate) mdl = rmdl.apply_to(internal_mdl) with pytest.raises(ModelErr) as excinfo: pvals = [p.val for p in internal_mdl.pars] mdl.calc(p=pvals) assert ModelErr.dict['nogrid'] in str(excinfo.value)
def _test_regrid1d_interpolation(rtol, eval_incr=True, req_incr=True, method=None, setup_1d=None): """Test interpolation case. Parameters ---------- rtol : number The relative tolerance, passed through to assert_allclose. eval_incr : bool, optional Does the evaluation grid increase or decrease? req_incr : bool, optional Does the requested grid increase or decrease? method : function reference, optional The interpolator to use, which should match sherpa.utils.linear_interp's API. If None then use the default method (which is sherpa.utils.neville) """ internal_mdl = setup_1d # Shift the evaluation grid compared to the # requested grid (making sure that the evaluation # grid extends outside the requested grid on both # edges to avoid checking for edge effects here). # # grid_evaluate is x'_i, has 22 bins # grid_request is x_i, has 21 bins # grid_evaluate = np.arange(-10, 100, 5) grid_request = np.linspace(-5, 85, 21) if not eval_incr: grid_evaluate = grid_evaluate[::-1] if not req_incr: grid_request = grid_request[::-1] rmdl = ModelDomainRegridder1D(EvaluationSpace1D(grid_evaluate)) if method is not None: rmdl.method = method mdl = rmdl.apply_to(internal_mdl) yexp = internal_mdl(grid_request) ygot = mdl(grid_request) assert_allclose(ygot, yexp, atol=0, rtol=rtol)
def test_regrid1d_error_grid_mismatch_2(setup_1d): """Internal grid is points but given integrated""" internal_mdl = setup_1d grid_evaluate = EvaluationSpace1D(np.arange(-10, 100, 5)) rmdl = ModelDomainRegridder1D(grid_evaluate) mdl = rmdl.apply_to(internal_mdl) grid_run = np.arange(0, 20, 10) with pytest.raises(ModelErr) as excinfo: mdl(grid_run[:-1], grid_run[1:]) assert ModelErr.dict['needspoint'] in str(excinfo.value)
def test_regrid1d_error_grid_mismatch_1(setup_1d): """Internal grid is integrated but given points""" internal_mdl = setup_1d grid_evaluate = np.arange(-10, 100, 5) eval_space = EvaluationSpace1D(grid_evaluate[:-1], grid_evaluate[1:]) rmdl = ModelDomainRegridder1D(eval_space) mdl = rmdl.apply_to(internal_mdl) grid_run = np.arange(0, 20, 10) with pytest.raises(ModelErr) as excinfo: mdl(grid_run) assert 'An integrated grid is required for model evaluation' \ in str(excinfo.value)
def test_regrid1d_no_overlap_rev3(setup_1d): """If the two grids have no overlap, return value is the same as the model evaluated over the data space.""" internal_mdl = setup_1d eval_space = EvaluationSpace1D(np.arange(1000, 2000, 100)[::-1]) rmdl = ModelDomainRegridder1D(eval_space) mdl = rmdl.apply_to(internal_mdl) grid = np.arange(-10, 100, 5)[::-1] ygot = mdl(grid) assert_allclose(ygot, [ 0., ] * grid.size, atol=0, rtol=1e-7)
def test_regrid1d_call_twice(setup_1d): """What happens if have no evaluation (output) grid?""" internal_mdl = setup_1d eval_space = EvaluationSpace1D(np.arange(1000, 2000, 100)) rmdl = ModelDomainRegridder1D(eval_space) mdl = rmdl.apply_to(internal_mdl) # It appears that calling the model with no arguments is # the same as calling 'rmdl(internal_mdl)'. An "easy" way # to test this is to rely on the stringification (and have # other tests handle that rmdl(...) is doing the right # thing). # noargs = mdl() assert str(mdl) == str(noargs)
def test_regrid1d_identity_after_clearing_grid(setup_1d): """Ensure that the grid can be removed.""" internal_mdl = setup_1d eval_space = EvaluationSpace1D(np.arange(200, 300, 20)) rmdl = ModelDomainRegridder1D(eval_space) mdl = rmdl.apply_to(internal_mdl) grid = np.arange(-10, 100, 5) yexp = internal_mdl(grid) rmdl.grid = None ygot = mdl(grid) assert_allclose(ygot, yexp, atol=0, rtol=1e-7)
def test_regrid1d_no_overlap_int(setup_1d): """If the two grids have no overlap, return value is the same as the model evaluated over the data space.""" internal_mdl = setup_1d array = np.arange(1000, 2000, 100) eval_space = EvaluationSpace1D(array[:-1], array[1:]) rmdl = ModelDomainRegridder1D(eval_space) mdl = rmdl.apply_to(internal_mdl) grid = np.arange(-10, 100, 5) with pytest.warns(UserWarning): ygot = mdl(grid[:-1], grid[1:]) assert_allclose(ygot, [ 50., ] * (grid.size - 1), atol=0, rtol=1e-7)
def test_regrid1d_int_flux(): """Check integration models using an XSPEC-style c[p]flux model. """ gamma = 1.7 pmdl = PowLaw1D() pmdl.gamma = gamma fluxmdl = ReNormalizerKernel1DInt() fluxmdl.flux = 20.0 fluxmdl.lo = 10.0 fluxmdl.hi = 20 grid = np.arange(1.0, 9.0, 0.01) glo = grid[:-1] ghi = grid[1:] mdl = fluxmdl(pmdl) regrid = ModelDomainRegridder1D() rmdl = regrid.apply_to(mdl) # ensure it covers the 10 - 20 range as well as 1-9. Pick # a smaller grid size than the output grid. # xfull = np.arange(0, 22, 0.005) regrid.grid = xfull[:-1], xfull[1:] y_regrid = rmdl(glo, ghi) assert y_regrid.shape == glo.shape # Model flux in lo/hi range is int_lo^hi x^-gamma dx (since norm # and reference of the power law are both 1.0). This is, since # gamma is not 1, [x^(1-gamma)]^hi_lo / (1 - gamma). # term = 1.0 - gamma renorm = (20**term - 10**term) / term y_expected = pmdl(glo, ghi) * 20 / renorm assert_allclose(y_regrid, y_expected, atol=1e-10, rtol=0)
def _test_regrid1d_int(rtol, eval_incr=True, req_incr=True, setup_1d=None): """Test with for integrated grids. Parameters ---------- rtol : number The relative tolerance, passed through to assert_allclose. eval_incr : bool, optional Does the evaluation grid increase or decrease? CURRENTLY UNSUPPORTED IF False. req_incr : bool, optional Does the requested grid increase or decrease? CURRENTLY UNSUPPORTED IF False. Notes ----- This is very similar to _test_regrid1d_interpolation except that it does not support the method parameter. """ # have not coded this, since need xlo[0] > xlo[1] but xhi[0] > xlo[0] # (I think). assert eval_incr assert req_incr internal_mdl = setup_1d grid_evaluate = np.arange(-10, 100, 5) grid_request = np.linspace(-5, 85, 21) eval_space = EvaluationSpace1D(grid_evaluate[:-1], grid_evaluate[1:]) rmdl = ModelDomainRegridder1D(eval_space) mdl = rmdl.apply_to(internal_mdl) yexp = internal_mdl(grid_request[:-1], grid_request[1:]) ygot = mdl(grid_request[:-1], grid_request[1:]) assert_allclose(ygot, yexp, atol=0, rtol=rtol)
def regrid(self, *args, **kwargs): """ The class RegriddableModel1D allows the user to evaluate in the requested space then interpolate onto the data space. An optional argument 'interp' enables the user to change the interpolation method. Examples -------- >>> import numpy as np >>> from sherpa.models.basic import Box1D >>> mybox = Box1D() >>> request_space = np.arange(1, 10, 0.1) >>> regrid_model = mybox.regrid(request_space, interp=linear_interp) """ valid_keys = ('interp', ) for key in kwargs.keys(): if key not in valid_keys: raise TypeError("unknown keyword argument: '%s'" % key) eval_space = EvaluationSpace1D(*args) regridder = ModelDomainRegridder1D(eval_space, **kwargs) regridder._make_and_validate_grid(args) return regridder.apply_to(self)
def test_regrid1d_wrapping_name(): """Check the name field of a wrapped model. This is also checked in test_regrid1d_wrapping_str. """ internal_model = Const1D('con') + Gauss1D('gau') imodel_name = internal_model.name # a test where the Regrid1D model is named is in # test_regrid1d_wrapping_str. rmdl = ModelDomainRegridder1D() mdl = rmdl.apply_to(internal_model) # TODO: It is not clear what the syntactic constraints on # the name field are; if it is to create an evaluable # model at the UI layer then the following is # incorrect. There is "prior art" here with PSF, ARF, # and RMF models to look at. # expected_name = 'regrid1d({})'.format(imodel_name) assert mdl.name == expected_name
def test_regrid1d_works_with_convolution_style(): """This doesn't really test more than the previous model-evaluation tests. """ smdl = StepLo1D() smdl.xcut = 100 smdl.ampl = 10 cmdl = Const1D() cmdl.c0 = -500 imdl = smdl + cmdl # Set up the convolution kernel # gsmooth = Gauss1D() psf = PSFModel('psf', gsmooth) smoothed = psf(imdl) # This is the model that will be evaluated # regrid = ModelDomainRegridder1D() smoothed_regrid = regrid.apply_to(smoothed) # Ignoring edge effects, the smoothed step function drops from # x=100 down to x~120 (it is not clear why it doesn't smooth # below x=100, but maybe I've set up the convolution wrong). # So, if the regrid model evaluates x=0 to 200 but the requested # grid is from x=102 ro 180, then we should see a difference # to evaluating without regrid. # xfull = np.arange(0, 200, 0.5) xout = np.arange(101, 180, 0.5) regrid.grid = xfull # fake up a data object for the fold method # TODO: it is not clear to me what grid should be used here; # xfull or xout # d = Data1D('fake', xfull, xfull * 0) psf.fold(d) y_regrid = smoothed_regrid(xout) # calculate the expected values y_full = smoothed(xfull) # since the grids have the same binning, it is a simple extraction idx0, = np.where(xfull == xout[0]) idx1, = np.where(xfull == xout[-1]) y_expected = y_full[idx0[0]:idx1[0] + 1] assert_allclose(y_regrid, y_expected, atol=1e-10, rtol=0) # check that this is all worth it; i.e. that without regrid # you just get the constant term. If this fails then it does not # mean that the regrid code is broken, but it implies that # something fundamental has changed with the basic model # evaluation. # d = Data1D('fake', xout, xout * 0) psf.fold(d) y_check = smoothed(xout) y_expected = np.zeros(xout.size) + cmdl.c0.val assert_allclose(y_check, y_expected, rtol=0, atol=1e-7)
def regrid(self, *arrays): eval_space = EvaluationSpace1D(*arrays) regridder = ModelDomainRegridder1D(eval_space) return regridder.apply_to(self)