コード例 #1
0
def test_fail_invalid_namefunc(namefunc):
    """We do basic checking on namefunc"""

    # The contents shouldn't be checked
    model = '/dev/null'

    with pytest.raises(ValueError) as ve:
        xspec.parse_xspec_model_description(model, namefunc)

    assert str(ve.value) == 'namefunc must be a callable which takes and returns a string'
コード例 #2
0
def test_fail_missing_parameters():
    """We have less parameters than required"""

    model = StringIO("""apec           3  0.         1.e20           C_apec    add  0
Abundanc " "    1.    0.      0.      5.        5.        -0.001

""")

    with pytest.raises(ValueError) as ve:
        xspec.parse_xspec_model_description(model)

    assert str(ve.value) == 'model=apec missing 2 parameters'
コード例 #3
0
def test_fail_invalid_model_line():
    """There is some check of a model line"""

    model = StringIO("""apec           3  0.         1.e20           C_apec    add
Abundanc " "    1.    0.      0.      5.        5.        -0.001

""")

    with pytest.raises(ValueError) as ve:
        xspec.parse_xspec_model_description(model)

    assert str(ve.value).startswith('Expected: modelname npars ')
コード例 #4
0
def test_fail_periodic_parameter():
    """We may add support for these in the future.

    """

    model = StringIO("""apec           1  0.         1.e20           C_apec    add  0
Abundanc " "    1.    0.      0.      5.        5.        -0.001 P
""")

    with pytest.raises(ValueError) as ve:
        xspec.parse_xspec_model_description(model)

    assert str(ve.value).startswith('Periodic parameters are unsupported; model=apec:\n')
コード例 #5
0
def test_fail_invalid_basic_parameter():
    """There is a check of model types.

    It might be better to just skip these.
    """

    model = StringIO("""apec           1  0.         1.e20           C_apec    sub  0
Abundanc " "    1.

""")

    with pytest.raises(ValueError) as ve:
        xspec.parse_xspec_model_description(model)

    assert str(ve.value).startswith('Expected 6 values after units; model=apec\n')
コード例 #6
0
def test_fail_unknown_model():
    """There is some check of model types

    It mmight be better to just skip these.
    """

    model = StringIO("""apec           1  0.         1.e20           C_apec    sub  0
Abundanc " "    1.    0.      0.      5.        5.        -0.001

""")

    with pytest.raises(ValueError) as ve:
        xspec.parse_xspec_model_description(model)

    assert str(ve.value).startswith('Unexpected model type sub in:\n')
コード例 #7
0
def test_create_model_additive():
    """Fake up an additive model"""

    model = StringIO("""apec           1  0.         1.e20           C_apec    add  0
kT      keV     1.    0.008   0.008   64.0      64.0      .01

""")
    parsed = xspec.parse_xspec_model_description(model)

    converted = xspec.create_xspec_code(parsed)

    # Very basic checks of the output
    #
    python = converted.python.split('\n')
    compiled = converted.compiled.split('\n')

    assert 'class XSapec(XSAdditiveModel):' in python
    assert '    _calc = _models.C_apec' in python
    assert "    def __init__(self, name='apec'):" in python
    assert "        self.kT = XSParameter(name, 'kT', 1.0, min=0.008, max=64.0, hard_min=0.008, hard_max=64.0, units='keV')" in python
    assert "        self.norm = Parameter(name, 'norm', 1.0, min=0.0, max=1e+24, hard_min=0.0, hard_max=1e+24)" in python
    assert '        XSAdditiveModel.__init__(self, name, (self.kT,self.norm))' in python

    assert converted.compiled.find('\nextern "C" {\n\n}\n') > -1
    assert '  XSPECMODELFCT_C_NORM(C_apec, 2),' in compiled
コード例 #8
0
def test_parse_model_dat(path):
    """Can we parse the model.dat file (via string or pathlike)

    There's limited checking of the data since we can't
    guarantee it won't change significantly over time.
    """

    from sherpa.astro.xspec import get_xspath_manager

    path = get_xspath_manager()
    mfile = os.path.join(path, 'model.dat')

    if path:
        mfile = pathlib.PurePath(mfile)

    parsed = xspec.parse_xspec_model_description(mfile)
    assert len(parsed) > 0

    # Check we only have Add, Mul, Con, and Acn types
    #
    mtypes = set()
    for model in parsed:
        mtypes.add(model.modeltype)

    assert mtypes == set(["Add", "Mul", "Con", "Acn"])
コード例 #9
0
def test_warn_parse_repeated_parname(caplog):
    """Check we warn if the parameter name is repeated"""

    model = StringIO("""apec           3  0.         1.e20           C_apec    add  0
Abundanc " "    1.    0.      0.      5.        5.        -0.001
kT      keV     1.    0.008   0.008   64.0      64.0      .01
abundanc " "    0.   -0.999  -0.999   10.       10.       -0.01

""")

    assert len(caplog.records) == 0
    xspec.parse_xspec_model_description(model)

    assert len(caplog.records) == 1
    lname, lvl, msg = caplog.record_tuples[0]
    assert lname == 'sherpa.astro.utils.xspec'
    assert lvl == logging.WARNING
    assert msg == 'model=apec re-uses parameter name abundanc'
コード例 #10
0
def test_fail_toomany_parameters():
    """We have more parameters than required

    This actually errors out as it tries to parse the extra
    parameter as a model (that is, this is only checked for
    indirectly).

    """

    model = StringIO("""apec           1  0.         1.e20           C_apec    add  0
Abundanc " "    1.    0.      0.      5.        5.        -0.001
Redshift " "    0.   -0.999  -0.999   10.       10.       -0.01

""")

    # As we don't have an explicit check the actual error depends on the
    # next line so it's not worth checking the message.
    #
    with pytest.raises(ValueError):
        xspec.parse_xspec_model_description(model)
コード例 #11
0
def test_convert_model_dat():
    """Can we convert the model.dat file?

    This is more just an existence test (i.e. does it work) with
    limited checking of the output. Checking the warnings that
    are created by parsing/converting the XSPEC model.dat is
    tricky to do reliably as it depends on the XSPEC version,
    so we do not check them (there are specific tests above).

    """

    from sherpa.astro.xspec import get_xspath_manager

    path = get_xspath_manager()
    mfile = os.path.join(path, 'model.dat')

    # We do not check the return value, just that it runs
    parsed = xspec.parse_xspec_model_description(mfile)
    xspec.create_xspec_code(parsed)
コード例 #12
0
def test_skip_repeated_model(caplog):
    """Technically we could use the same model but with different
    parameters but there was a bug in a version of XSPEC where
    the same model was used incorrectly, so check we handle this.

    We only skip it when we come to creating the code.

    """

    model = StringIO("""apec           1  0.         1.e20           C_foo     mul  0
Abundanc " "    1.    0.      0.      5.        5.        -0.001

fred           0  0.         1.e20           C_foo     mul  0
""")

    parsed = xspec.parse_xspec_model_description(model)
    assert len(parsed) == 2
    assert len(caplog.records) == 0

    # As an extra benefit as we skip both models we can check the
    # 'no valid models' error.
    #
    with pytest.raises(ValueError) as ve:
        xspec.create_xspec_code(parsed)

    assert str(ve.value) == 'No supported models were found!'

    assert len(caplog.records) == 2
    lname, lvl, msg = caplog.record_tuples[0]
    assert lname == 'sherpa.astro.utils.xspec'
    assert lvl == logging.WARNING
    assert msg == 'Skipping model apec as it calls foo which is used by 2 different models'

    lname, lvl, msg = caplog.record_tuples[1]
    assert lname == 'sherpa.astro.utils.xspec'
    assert lvl == logging.WARNING
    assert msg == 'Skipping model fred as it calls foo which is used by 2 different models'
コード例 #13
0
def test_create_model_convolution():
    """Fake up a convolution model"""

    model = StringIO("""rgsxsrc        1  0.         1.e20           rgsxsrc   con  0
order    " "  -1.   -3.    -3.      -1.       -1.       -1

""")
    parsed = xspec.parse_xspec_model_description(model)

    converted = xspec.create_xspec_code(parsed)

    # Very basic checks of the output
    #
    python = converted.python.split('\n')
    compiled = converted.compiled.split('\n')

    assert 'class XSrgsxsrc(XSConvolutionKernel):' in python
    assert '    _calc = _models.rgsxsrc' in python
    assert "    def __init__(self, name='rgsxsrc'):" in python
    assert "        self.order = XSParameter(name, 'order', -1.0, min=-3.0, max=-1.0, hard_min=-3.0, hard_max=-1.0, frozen=True)" in python
    assert '        XSConvolutionKernel.__init__(self, name, (self.order,))' in python

    assert '  void rgsxsrc_(float* ear, int* ne, float* param, int* ifl, float* photar, float* photer);' in compiled
    assert '  XSPECMODELFCT_CON_F77(rgsxsrc, 1),' in compiled
コード例 #14
0
def test_create_model_multiplicative():
    """Fake up a multplicative model"""

    model = StringIO("""abcd           1  0.         1.e20           foos    mul  0
nH      cm^-3   1.0   1.e-6  1.e-5  1.e19  1.e20   -0.01

""")
    parsed = xspec.parse_xspec_model_description(model)

    converted = xspec.create_xspec_code(parsed)

    # Very basic checks of the output
    #
    python = converted.python.split('\n')
    compiled = converted.compiled.split('\n')

    assert 'class XSabcd(XSMultiplicativeModel):' in python
    assert '    _calc = _models.foos' in python
    assert "    def __init__(self, name='abcd'):" in python
    assert "        self.nH = XSParameter(name, 'nH', 1.0, min=1e-05, max=1e+19, hard_min=1e-06, hard_max=1e+20, frozen=True, units='cm^-3')" in python
    assert '        XSMultiplicativeModel.__init__(self, name, (self.nH,))' in python

    assert '  void foos_(float* ear, int* ne, float* param, int* ifl, float* photar, float* photer);' in compiled
    assert '  XSPECMODELFCT(foos, 1),' in compiled
コード例 #15
0
        except TypeError:
            continue

        if name not in seen:
            print(f"Unable to find {name} in model.dat")


if __name__ == "__main__":

    # Too lazy to use python argument module
    hard = True
    argv = []
    for arg in sys.argv[1:]:
        if arg == '--nohard':
            hard = False
        else:
            argv.append(arg)

    if len(argv) != 1:
        sys.stderr.write(f"Usage: {sys.argv[0]} infile\n")
        sys.stderr.write("       --nohard  do not check hard ranges\n\n")
        sys.exit(1)

    # This errors out in case of significant model-support issues
    # (e.g. a periodic model parameter) but can also just be an
    # issue with a poorly-specified interchange format (the model.dat
    # file) which may just need changes to this routine.
    #
    models = parse_xspec_model_description(argv[0])
    compare_xspec_models(models, hard=hard)
コード例 #16
0
    """

    for amodel in models:
        if amodel.name == modelname:
            model = amodel
            break
    else:
        raise ValueError(f"Unknown model name: {modelname}")

    code = xspec.create_xspec_code([model])
    print("# C++ code for sherpa/astro/xspec/src/_xspec.cc\n")
    print(code.compiled)
    print("\n# Python code for sherpa/astro/xspec/__init__.py\n")
    print(code.python)


if __name__ == "__main__":

    if len(sys.argv) != 3:
        sys.stderr.write(f"Usage: {sys.argv[0]} infile model\n")
        sys.exit(1)

    # This errors out in case of significant model-support issues
    # (e.g. a periodic model parameter) but can also just be an
    # issue with a poorly-specified interchange format (the model.dat
    # file) which may just need changes to this routine.
    #
    models = xspec.parse_xspec_model_description(sys.argv[1])
    create_xspec_model(models, sys.argv[2])
コード例 #17
0
def test_parse_multiplicative_model():
    """Can we parse a Multiplicative model"""

    model = StringIO("""zdust          4   0.         1.e20          mszdst    mul  0 1
$method   " "   1       1       1       3       3       -0.01
E_BmV    " "   0.1     0.0     0.0     100.    100.     0.01
*redshift " "     0.0
break  Hz   2.42E17 1.E10   1.E15     1.E19    1.E25    1E10
""")

    parsed = xspec.parse_xspec_model_description(model)
    assert len(parsed) == 1
    model = parsed[0]
    assert model.name == 'zdust'
    assert model.clname == 'XSzdust'
    assert model.modeltype == 'Mul'
    assert model.flags == pytest.approx([0, 1])
    assert model.elo == pytest.approx(0)
    assert model.ehi == pytest.approx(1e20)
    assert model.initString is None
    assert model.funcname == 'mszdst'
    assert model.language == 'Fortran - single precision'

    assert len(model.pars) == 4

    assert model.pars[0].paramtype == 'Switch'
    assert model.pars[0].name == 'method'
    assert model.pars[0].units is None
    assert model.pars[0].default == pytest.approx(1)
    assert model.pars[0].softmin == pytest.approx(1)
    assert model.pars[0].hardmin == pytest.approx(1)
    assert model.pars[0].softmax == pytest.approx(3)
    assert model.pars[0].hardmax == pytest.approx(3)

    assert model.pars[1].paramtype == 'Basic'
    assert model.pars[1].name == 'E_BmV'
    assert not model.pars[1].frozen
    assert model.pars[1].units is None
    assert model.pars[1].default == pytest.approx(0.1)
    assert model.pars[1].softmin == pytest.approx(0.0)
    assert model.pars[1].hardmin == pytest.approx(0.0)
    assert model.pars[1].softmax == pytest.approx(100.0)
    assert model.pars[1].hardmax == pytest.approx(100.0)

    assert model.pars[2].paramtype == 'Scale'
    assert model.pars[2].name == 'redshift'
    assert model.pars[2].units is None
    assert model.pars[2].default == pytest.approx(0)
    assert model.pars[2].softmin is None
    assert model.pars[2].hardmin is None
    assert model.pars[2].softmax is None
    assert model.pars[2].hardmax is None

    assert model.pars[3].paramtype == 'Basic'
    assert model.pars[3].name == 'break_'
    assert not model.pars[3].frozen
    assert model.pars[3].units == 'Hz'
    assert model.pars[3].default == pytest.approx(2.42e17)
    assert model.pars[3].softmin == pytest.approx(1e15)
    assert model.pars[3].hardmin == pytest.approx(1e10)
    assert model.pars[3].softmax == pytest.approx(1e19)
    assert model.pars[3].hardmax == pytest.approx(1e25)
コード例 #18
0
def test_parse_additive_model():
    """Can we parse an Additive model"""

    model = StringIO("""apec           3  0.         1.e20           C_apec    add  0
kT      keV     1.    0.008   0.008   64.0      64.0      .01
Abundanc " "    1.    0.      0.      5.        5.        -0.001
Redshift " "    0.   -0.999  -0.999   10.       10.       -0.01

""")

    parsed = xspec.parse_xspec_model_description(model)
    assert len(parsed) == 1
    model = parsed[0]
    assert model.name == 'apec'
    assert model.clname == 'XSapec'
    assert model.modeltype == 'Add'
    assert model.flags == pytest.approx([0])
    assert model.elo == pytest.approx(0)
    assert model.ehi == pytest.approx(1e20)
    assert model.initString is None
    assert model.funcname == 'apec'
    assert model.language == 'C++ style'

    assert len(model.pars) == 4

    for par in model.pars:
        assert par.paramtype == 'Basic'

    assert model.pars[0].name == 'kT'
    assert not model.pars[0].frozen
    assert model.pars[0].units == 'keV'
    assert model.pars[0].default == pytest.approx(1)
    assert model.pars[0].softmin == pytest.approx(0.008)
    assert model.pars[0].hardmin == pytest.approx(0.008)
    assert model.pars[0].softmax == pytest.approx(64.0)
    assert model.pars[0].hardmax == pytest.approx(64.0)

    assert model.pars[1].name == 'Abundanc'
    assert model.pars[1].frozen
    assert model.pars[1].units is None
    assert model.pars[1].default == pytest.approx(1)
    assert model.pars[1].softmin == pytest.approx(0.0)
    assert model.pars[1].hardmin == pytest.approx(0.0)
    assert model.pars[1].softmax == pytest.approx(5.0)
    assert model.pars[1].hardmax == pytest.approx(5.0)

    assert model.pars[2].name == 'Redshift'
    assert model.pars[2].frozen
    assert model.pars[2].units is None
    assert model.pars[2].default == pytest.approx(0)
    assert model.pars[2].softmin == pytest.approx(-0.999)
    assert model.pars[2].hardmin == pytest.approx(-0.999)
    assert model.pars[2].softmax == pytest.approx(10.0)
    assert model.pars[2].hardmax == pytest.approx(10.0)

    assert model.pars[3].name == 'norm'
    assert not model.pars[3].frozen
    assert model.pars[3].units is None
    assert model.pars[3].default == pytest.approx(1)
    assert model.pars[3].softmin == pytest.approx(0)
    assert model.pars[3].hardmin == pytest.approx(0)
    assert model.pars[3].softmax == pytest.approx(1e24)
    assert model.pars[3].hardmax == pytest.approx(1e24)