Package: parse-xspec
Parsing XSPEC filesThis is ahighly experimentalpackage to support parsingXSPECfiles - at
present only the
"model.dat"
format - into a structured form. It has been extracted from code
written by SDS atCXCto support XSPEC
users, and which has recently been added toSherpainhttps://github.com/sherpa/sherpa/pull/1260InstallationHopefully it's as simple aspip install parse_xspecot you can try installing directly fromGitHub.Aim>>> from io import StringIO
>>> from parse_xspec.models import parse_xspec_model_description
>>> model = StringIO("""agauss         2   0.         1.e20          C_agauss  add  0
LineE   A      10.0   0.      0.      1.e6      1.e6      0.01
Sigma   A      1.0    0.      0.      1.e6      1.e6      0.01

""")
>>> parsed = parse_xspec_model_description(model)
>>> len(parsed)
1
>>> parsed[0]
AddModelDefinition('agauss','C_agauss',[0],0.0,1e+20,[BasicParameterDefinition('LineE', 10.0, units='A', softmin=0.0, softmax=1000000.0, hardmin=0.0, hardmax=1000000.0, delta=0.01), BasicParameterDefinition('Sigma', 1.0, units='A', softmin=0.0, softmax=1000000.0, hardmin=0.0, hardmax=1000000.0, delta=0.01)])
>>> parsed[0].name
'agauss'
>>> parsed[0].funcname
'agauss'
>>> parsed[0].language
'C++ style'
>>> parsed[0].modeltype
'Add'
>>> parsed[0].flags
[0]
>>> parsed[0].initString is None
True
>>> parsed[0].pars
[BasicParameterDefinition('LineE', 10.0, units='A', softmin=0.0, softmax=1000000.0, hardmin=0.0, hardmax=1000000.0, delta=0.01), BasicParameterDefinition('Sigma', 1.0, units='A', softmin=0.0, softmax=1000000.0, hardmin=0.0, hardmax=1000000.0, delta=0.01)]
>>> parsed[0].pars[0].paramtype
'Basic'
>>> parsed[0].pars[0].name
'LineE'
>>> parsed[0].pars[0].units
'A'
>>> parsed[0].pars[0].default
10.0
>>> parsed[0].pars[0].softmin
0.0
>>> parsed[0].pars[0].hardmax
1000000.0
>>> parsed[0].pars[0].delta
0.01
>>> parsed[0].pars[0].frozen
FalseThe idea is that we have a structured representation of the data.Let's see what happens when we querh the HEASOFT 6.29 model.dat file
(you can just use a string but I want to check out the pathlib
handling):>>> import os
>>> from pathlib import Path
>>> headas = os.getenv('HEADAS')
>>> p = headas
>>> models = p / '..' / 'spectral' / 'manager' / 'model.dat'
>>> parsed = parse_xspec_model_description(models)
model=vvwdem re-uses parameter name p
model=ismabs re-uses parameter name sii
model=ismabs re-uses parameter name siii
>>> len(parsed)
232So, this has reported three cases where the parameter names are the
same,ifyou ignore the case of the parameters (which is a feature
of the Sherpa model interface). TheISMABScase is a fun one because we haveSiIandSIIas well asSiIIandSiIII.One bug I found and reported in an old version of XSPEC was that
the same function was being used for multiple models (this is technically
possible but it seems unlikely to be what was meant). We can check
with code like the following:>>> ctr = Counter([p.funcname for p in parsed])
>>> [n for n, c in ctr.items() if c > 1]
[]What is the most-popular parameter name? Is itnHorHe?>>> pnames = []
>>> for model in parsed:
...     for par in model.pars:
...         pnames.append(par.name)
...
>>> ctr = Counter(pnames)
>>> ctr.most_common(5)
[('Redshift', 100), ('kT', 49), ('Fe', 42), ('O', 41), ('He', 40)]It's not really different if we ignore case:>>> lnames = []
>>> for model in parsed:
...     for par in model.pars:
...         lnames.append(par.name.lower())
...
>>> ctr = Counter(lnames)
>>> ctr.most_common(5)
[('redshift', 124), ('kt', 49), ('fe', 42), ('n', 41), ('o', 41)]How many convolution models are there?>>> ctr = Counter([p.modeltype for p in parsed])
>>> ctr
Counter({'Add': 148, 'Mul': 61, 'Con': 22, 'Acn': 1})What is the break-down of model "interfaces" (that is C vs C++ vs
FORTRAN)?>>> ctr = Counter([p.language for p in parsed])
>>> ctr
Counter({'C++ style': 135, 'Fortran - single precision': 89, 'C style': 8})The use of a string rather than an enumeration bugs me and I may
change it!What "special" parameters are used (that is scale or switch):>>> for model in parsed:
...     for par in model.pars:
...         if par.paramtype == 'Basic':
...             continue
...         print(par)
...
switch = 1
switch = 1
switch = 1
switch = 1
switch = 1
switch = 1
switch = 1
energy00 = 0.5 units=keV
energy01 = 1.0 units=keV
energy02 = 1.5 units=keV
energy03 = 2.0 units=keV
energy04 = 3.0 units=keV
energy05 = 4.0 units=keV
energy06 = 5.0 units=keV
energy07 = 6.0 units=keV
energy08 = 7.0 units=keV
energy09 = 8.0 units=keV
switch = 2
model = 1
rflag = 1
lflag = 0
pivotE = 1.0 units=keV
switch = 1
switch = 1
specfile = 1200
specfile = 1200
specfile = 6
nmax = 1.0
FAST = 0.0
lflag = 1
vflag = 1
switch = 1
switch = 2
switch = 1
switch = 1
switch = 2
switch = 2
switch = 2
rflag = 1
lflag = 1
pivotE = 1.0 units=keV
DustModel = 1
method = 1
redshift = 0.0
model = 0
lyman_limit = 1
nregions = 1.0
fracexpo = 1.0How many parameters have a hard maximum different to their soft
maximum (skipping scale and switch parameters)?>>> count = 0
>>> for model in parsed:
...     for par in model.pars:
...         if par.paramtype != 'Basic':
...             continue
...         if par.hardmax == par.softmax:
...             continue
...         count += 1
...
>>> count
391How many parameters start at their hard-minimum value?>>> count = 0
>>> for model in parsed:
...     for par in model.pars:
...         if par.paramtype != 'Basic' or par.default > par.hardmin:
...             continue
...         count += 1
...
>>> count
117If you usesoftmininstead you get 124.