Can we take advantage of the interactive facilities added in IPython 2.0?

Here I play with the IPython widgets - added in 2.0 - to see whether it makes visualizing how model parameters change the predicted spectrum.

Set up

In [1]:
from sherpa.astro.ui import *
from pycrates import *
from pychips import *
from pychips.hlui import *
In [2]:
# Sherpa settings
import logging
logging.getLogger('sherpa').propagate = False

import sys
sys.tracebacklimit = None
In [3]:
# ChIPS settings
set_preference('window.display', 'false')

from IPython.core.display import Image

# There is a potential for confusion with IPython.display used below
import IPython.core.display

import tempfile
import os

def my_print(fname=None):
    "Display the current ChIPS window in IPython as a PNG"
    use_temp = False
    if fname is None:
        fname = tempfile.mkstemp(prefix='chips_temp', suffix='.png')[1]

    # Get ChIPS to create a PNG of the current window
    print_window(fname, 'clobber=true')
    img = Image(filename=fname)
    IPython.core.display.display_png(img)
    if use_temp:
        os.remove(fname)
        
def show_gui():
    print("show_gui has been disabled from the notebook")

Ready to go

In this notebook directory I have placed a few files; for instance a source spectrum, along with the response files (ARF and RMF), but no background file.

In [4]:
ls src*
src.arf  src.pi   src.rmf

Set up the spectrum and model

This is not meant to be a scientifically valid - or motivated - fit (I am ignoring the background, for one).

In [5]:
load_pha('src.pi')
group_counts(20)
notice(0.5,7)
read ARF file src.arf
read RMF file src.rmf
In [6]:
set_source(xsphabs.gal * xsapec.clus)
print(get_source())
(xsphabs.gal * xsapec.clus)
   Param        Type          Value          Min          Max      Units
   -----        ----          -----          ---          ---      -----
   gal.nH       thawed            1            0       100000 10^22 atoms / cm^2
   clus.kT      thawed            1        0.008           64        keV
   clus.Abundanc frozen            1            0            5           
   clus.redshift frozen            0            0           10           
   clus.norm    thawed            1            0        1e+24           
In [7]:
fit()
Dataset               = 1
Method                = levmar
Statistic             = chi2gehrels
Initial fit statistic = 3.41269e+09
Final fit statistic   = 242.307 at function evaluation 69
Data points           = 45
Degrees of freedom    = 42
Probability [Q-value] = 5.51961e-30
Reduced statistic     = 5.7692
Change in statistic   = 3.41269e+09
   gal.nH         1.28704     
   clus.kT        1.00295     
   clus.norm      0.000504721 

And a look at the result. I do not expect it to be that good since I picked the model somewhat at random and no background has been subtracted:

In [8]:
plot_fit()
my_print()

Now I create the code that will let us adjust the model parameters and have the changes automatically reflected in the plot.

In [9]:
#from IPython.html.widgets import interact, interactive, fixed
#from IPython.display import display
#from IPython.html.widgets import FloatSliderWidget, HTMLWidget

from IPython.html.widgets import interact
from IPython.html.widgets import FloatSliderWidget
In [10]:
# An interesting issue is how best to define the range, and step size, for a parameter;
# the obvious option is to use the min/max range and hope that the user has changed it
# to something sensible. Or, for large ranges, perform a logarithmic mapping from
# the slider value to the data (i.e. have the slider work on the log of the parameter
# value). This still doesn't help with the step size.
#
# For now I manually choose an "interesting" and "simple" range.
#
#nhrange = (0, 1, 0.005)
#ktrange = (0.1, 10, 0.1)
#arange = (0, 2, 0.05)
#zrange = (0, 1, 0.01)

nhrange = FloatSliderWidget(min=0, max=2, step=0.005, value=gal.nh.val)
ktrange = FloatSliderWidget(min=0.1, max=10, step=0.1, value=clus.kt.val)
arange = FloatSliderWidget(min=0, max=2, step=0.05, value=clus.abundanc.val)
zrange = FloatSliderWidget(min=0, max=1, step=0.01, value=clus.redshift.val)

def change_model(nh=1.287, kt=1.002946, abundance=1.0, redshift=0.0, xlog=False, ylog=False):
    "Play with a few model parameters"
    
    gal.nh = nh
    clus.kt = kt
    clus.abundanc = abundance
    clus.redshift = redshift
    plot_fit()
    if xlog:
        log_scale(X_AXIS)
    if ylog:
        log_scale(Y_AXIS)
    my_print()

A simple test:

In [11]:
change_model(nh=0.2)

The following creates 4 slider widgets that allow you to change the parameter value and have the results updated in the plot. Note that it's also clever enough to create check boxes for the xlog and ylog parameters without being asked.

It's a bit clunky/flickery, and you don't get to see the widgets in the "hardcopy" outputs of this notebook, so I have a screen shot following this.

In [12]:
interact(change_model, nh=nhrange, kt=ktrange, abundance=arange, redshift=zrange)

Here's a screen shot of the widgets:

In [13]:
IPython.display.Image('sliders.png')
Out[13]: