Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using BXA chains for error analysis #42

Open
pboorm opened this issue Dec 28, 2022 · 2 comments
Open

Using BXA chains for error analysis #42

pboorm opened this issue Dec 28, 2022 · 2 comments

Comments

@pboorm
Copy link

pboorm commented Dec 28, 2022

  • BXA version: 4.0.5
  • UltraNest version: 3.3.3
  • Python version: 3.8.12
  • Xspec version: 12.12.1
  • Operating System: macOS Monterey 12.6

Description

When using the BXA-generated chain files in Xspec after running a BXA fit, Xspec cannot estimate uncertainties from the chain with e.g., "flux 2. 10. err 1000". The issue seems to be that the BXA chain file column headings are incompatible with the model used.

What I Did

The model fit was:

Model constant<1>*TBabs<2>(zTBabs<3>*cabs<4>*zpowerlw<5> + zgauss<6>) Source No.: 1   Active/On
Model Model Component  Parameter  Unit     Value
 par  comp
   1    1   constant   factor              1.00000      frozen
   2    2   TBabs      nH         10^22    4.00000E-02  frozen
   3    3   zTBabs     nH         10^22    37.1630      +/-  0.0          
   4    3   zTBabs     Redshift            5.00000E-02  frozen
   5    4   cabs       nH         10^22    37.1630      = p3
   6    5   zpowerlw   PhoIndex            1.88462      +/-  0.0          
   7    5   zpowerlw   Redshift            5.00000E-02  frozen
   8    5   zpowerlw   norm                1.37403E-03  +/-  0.0          
   9    6   zgauss     LineE      keV      6.41373      +/-  0.0          
  10    6   zgauss     Sigma      keV      8.41334E-02  +/-  0.0          
  11    6   zgauss     Redshift            5.00000E-02  frozen
  12    6   zgauss     norm                1.58220E-05  +/-  0.0          

And the free parameter numbers in the BXA fit were: 3, 6, 8, 9, 10, 12. The corresponding chain.fits file generated by BXA had column headings (note the nH prior was added out of order of the other parameters):

PhoIndex__6, log(norm)__20, log(nH)__27, LineE__45, log(Sigma)__58, log(norm)__72, FIT_STATISTIC

The problem was fixed by manually changing the BXA chain file column headings, column orders and parameter units to match an equivalent Xspec-generated MCMC chain file:

Columns = nH__3, PhoIndex__6, norm__8, LineE__9, Sigma__10, norm__12, FIT_STATISTIC
Units = 10^22, , , keV, keV, , C-Statistic
@JohannesBuchner
Copy link
Owner

I don't quite understand how the numbers work.

Here is the current code:
https://github.com/JohannesBuchner/BXA/blob/master/bxa/xspec/solver.py#L64

Previous issues:
#12
#10

There is probably a way to get the number out from the model and parameter in the transformation list.

@pboorm
Copy link
Author

pboorm commented Jan 3, 2023

Thanks! I had a go at tweaking the store_chain() function. A version I wrote that appears to work for models with one data group is:

def store_chain_universal(chainfilename, transformations, posterior, fit_statistic):
	"""Writes a MCMC chain file in the same format as the Xspec chain command."""
	import astropy.io.fits as pyfits

	group_index = 1
	old_model = transformations[0]['model']

	indices = [t["index"] for t in transformations]
	names = []
	for t in transformations:
		if t['model'] != old_model:
			group_index += 1
		old_model = t['model']
		original_parname = AllModels(1)(t["index"]).name
		names.append('%s__%d' % (original_parname, t['index']))# + (group_index - 1) * old_model.nParameters))

	columns = [pyfits.Column(
		name=name, format='D', unit=AllModels(1)(transformations[i]["index"]).unit, array=t['aftertransform'](posterior[:, i]))
		for i, name in enumerate(names)]
	columns = list(np.array(columns)[np.argsort(indices)])

	columns.append(pyfits.Column(name='FIT_STATISTIC', format='D', array=fit_statistic))
	table = pyfits.ColDefs(columns)
	header = pyfits.Header()
	header.add_comment("""Created by BXA (Bayesian X-ray spectal Analysis) for Xspec""")
	header.add_comment("""refer to https://github.com/JohannesBuchner/""")
	header['TEMPR001'] = 1.
	header['STROW001'] = 1
	header['EXTNAME'] = 'CHAIN'
	tbhdu = pyfits.BinTableHDU.from_columns(table, header=header)
	tbhdu.writeto(chainfilename, overwrite=True)

This saves the column names as the original parameter names (e.g., before adding extensions around the parameter name that depend on the prior) as well as the units stored in the model parameters. However, the code is using "AllModels(1)", so will not work for models with multiple datagroups/models. I've left the corresponding code commented that I think was being used to account for this in the original version (i.e. here:

names.append('%s__%d' % (t['name'], t['index'] + (group_index - 1) * old_model.nParameters))
).

When I load the chain generated with this function, the error on flux can be computed from the chain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants