About Chandra Archive Proposer Instruments & Calibration Newsletters Data Analysis HelpDesk Calibration Database NASA Archives & Centers Chandra Science Links

Skip the navigation links
Last modified: 29 April 2009
Hardcopy (PDF): A4 | Letter

Independent Background Responses

Sherpa Threads (CIAO 4.1)

[Python Syntax]



Overview

Last Update: 29 Apr 2009 - new script command is available with CIAO 4.1.2

Synopsis:

If you would like to fit a background-subtracted source spectrum using a common RMF and ARF for source and background, simply read the source spectrum fits file into Sherpa, subtract the background, and fit it. To fit source and background spectra simultaneously with proper and distinct RMFs and ARFs, load the source and background as different data sets. This thread illustrates both cases.

While the sample datafiles used in this thread are available as sherpa.tar.gz, note also that they are generated with the Using psextract to Extract ACIS Spectra and Response Files for Pointlike Sources thread.

Proceed to the HTML or hardcopy (PDF: A4 | letter) version of the thread.




Contents



Statistical Issues: Background Subtraction

A typical dataset may contain multiple spectra, one of which contains contributions from the source and the background, and one or more which contain background counts alone. (The background itself may contain contributions from the cosmic X-ray background, the particle background, an so on, but we ignore this complication.)

The proper way to treat background data is to model them. However, many X-ray astronomers subtract background data from the raw data.

Why should one not subtract background?

  • It reduces the amount of statistical information in the analysis - the final fit parameter values will be a less accurate estimate of the true values.
  • The background subtracted data are not Poisson-distributed; one cannot fit them with the Poisson likelihood or the Cash statistic, even in the low-count limit. For example, subtracting a background can give negative counts; this is definitely not Poissonian!
  • Fluctuations, particularly in the vicinity of localized features, can adversely affect analysis.


Fit a Background-Subtracted Source

Reading FITS data for source and background

By default, psextract updates the header keywords BACKFILE, RESPFILE, and ANCRFILE in the source spectrum file:

unix% dmkeypar 3c273.pi BACKFILE echo+
3c273_bg.pi

unix% dmkeypar 3c273.pi RESPFILE echo+
3c273.rmf

unix% dmkeypar 3c273.pi ANCRFILE echo+
3c273.arf

The RESPFILE and ANCRFILE keywords are not updated in the ungrouped background file, however, and should be manually added with dmhedit, as shown in the Update File Headers section of the psextract thread.

On account of these keywords, Sherpa automatically reads in the (ungrouped) background spectrum "3c273_bg.pi" and the source response matrices "3c273.rmf" and "3c273.arf" when the source spectrum is read:

sherpa> load_pha("3c273.pi")
statistical errors were found in file '3c273.pi' 
but not used; to use them, re-read with use_errors=True
read ARF file 3c273.arf
read RMF file 3c273.rmf
statistical errors were found in file '3c273_bg.pi' 
but not used; to use them, re-read with use_errors=True
read background file 3c273_bg.pi

If Sherpa does not automatically read in the background data, then it can be input as follows:

sherpa> load_bkg("3c273_bg.pi")
          
statistical errors were found in file '3c273_bg.pi' 
but not used; to use them, re-read with use_errors=True

Subtracting the background

The user can then subtract the background from the source spectrum and fit the background-subtracted source spectrum using response matrices for the source only. The following commands may be used as an example script for subtracting the background and then visualizing the 0.3-3 keV background-subtracted source data in Sherpa:

sherpa> subtract()
sherpa> plot_data()
sherpa> ignore()
sherpa> notice(0.3, 3.)
sherpa> plot_data()
sherpa> log_scale(X_AXIS)
sherpa> log_scale(Y_AXIS)
sherpa> limits(Y_AXIS, 1.e-3, 0.2)
sherpa> limits(X_AXIS, 0.2, 3.5)

Which produces Figure 1.

[Plot of the background-subtracted source spectrum]
[Print media version: Plot of the background-subtracted source spectrum]

Figure 1: The background-subtracted source spectrum

Plot of background-subtracted spectrum of 3c273


Defining the source instrument response

Since the header of the input source spectrum file referenced the instrument response files, an instrument response was automatically defined:

sherpa> print(get_arf())
name     = 3c273.arf
energ_lo = Float64[1090]
energ_hi = Float64[1090]
specresp = Float64[1090]
bin_lo   = None
bin_hi   = None
exposure = 38570.6924812

sherpa> print(get_rmf())
name     = 3c273.rmf
detchans = 1024
energ_lo = Float64[1090]
energ_hi = Float64[1090]
n_grp    = Int16[1090]
f_chan   = UInt32[2004]
n_chan   = UInt32[2004]
matrix   = Float64[58564]
offset   = 1
e_min    = Float64[1024]
e_max    = Float64[1024]

sherpa> print(get_bkg_arf())
name     = 3c273.arf
energ_lo = Float64[1090]
energ_hi = Float64[1090]
specresp = Float64[1090]
bin_lo   = None
bin_hi   = None
exposure = 38570.6924812

sherpa> print(get_bkg_rmf()
name     = 3c273.rmf
detchans = 1024
energ_lo = Float64[1090]
energ_hi = Float64[1090]
n_grp    = Int16[1090]
f_chan   = UInt32[2004]
n_chan   = UInt32[2004]
matrix   = Float64[58564]
offset   = 1
e_min    = Float64[1024]
e_max    = Float64[1024]

Note that the instrument response was automatically set up for both the source and background. However, unless a background model is defined for fitting, using the set_bkg_model command, then the background data will be ignored. If you manually read in a background file after reading the source data, the background instrument response will be reset. To set it to be identical to the source instrument response, use load_bkg_arf and load_bkg_rmf with the source response files:

sherpa> load_bkg_rmf("3c273.rmf")
sherpa> load_bkg_arf("3c273.arf")

sherpa> print(get_bkg_arf())
name     = 3c273.arf
energ_lo = Float64[1090]
energ_hi = Float64[1090]
specresp = Float64[1090]
bin_lo   = None
bin_hi   = None
exposure = 38570.6924812

sherpa> print(get_bkg_rmf())
name     = 3c273.rmf
detchans = 1024
energ_lo = Float64[1090]
energ_hi = Float64[1090]
n_grp    = Int16[1090]
f_chan   = UInt32[2004]
n_chan   = UInt32[2004]
matrix   = Float64[58564]
offset   = 1
e_min    = Float64[1024]
e_max    = Float64[1024]

Defining and fitting a source model

The following commands may be used as an example script for fitting the background-subtracted data with a source model consisting of an absorbed power law:

sherpa> set_source(xswabs.abs1*powlaw1d.p1)
sherpa> show_source()
Model: 1
(xswabs.abs1 * powlaw1d.p1)
   Param        Type          Value          Min          Max      Units
   -----        ----          -----          ---          ---      -----
   abs1.nh      thawed            1            0       100000 10^22 atoms / cm^2
   p1.gamma     thawed            1          -10           10           
   p1.ref       frozen            1 -3.40282e+38  3.40282e+38           
   p1.ampl      thawed            1            0  3.40282e+38  

sherpa> abs1.nh = 0.1
sherpa> guess(p1) 
WARNING: data set 1 has associated backgrounds, but they have not been subtracted, nor have background models been set

sherpa> fit()
 Solar Abundance Vector set to angr:  Anders E. & Grevesse N. Geochimica et Cosmochimica Acta 53, 197 (1989)
 Cross Section Table set to bcmc:  Balucinska-Church and McCammon, 1998
Data Set               = 1
Method                = levmar
Statistic             = chi2gehrels
Initial fit statistic = 89.8323
Final fit statistic   = 15.478 at function evaluation 94
Data points           = 33
Degrees of freedom    = 30
Probability [Q-value] = 0.986745
Reduced statistic     = 0.515934
Change in statistic   = 74.3543
   abs1.nh        0           
   p1.gamma       1.68306     
   p1.ampl        0.000160777 


sherpa> plot_fit_resid()

sherpa> log_scale(X_AXIS)
sherpa> log_scale(Y_AXIS)

sherpa> limits(Y_AXIS, 1.e-3, 0.2)
sherpa> limits(X_AXIS, 0.2, 3.5)

The results are shown Figure 2.

[Plot of the fit to the background-subtracted source spectrum]
[Print media version: Plot of the fit to the background-subtracted source spectrum]

Figure 2: Fit of the background-subtracted data

Plot of the source model fit to the background-subtracted spectrum of 3c273



Simultaneously Fit Source and Background with the Same Responses

Instead of subtracting the background, the user may choose to simultaneously fit the source and background spectra, using the same (source) RMF and ARF.

Reading FITS data for source and background

Again, Sherpa automatically reads in the (ungrouped) background spectrum and the source response matrices when the source spectrum is read:

sherpa> clean()
sherpa> load_pha("3c273.pi")
statistical errors were found in file '3c273.pi' 
but not used; to use them, re-read with use_errors=True
read ARF file 3c273.arf
read RMF file 3c273.rmf
statistical errors were found in file '3c273_bg.pi' 
but not used; to use them, re-read with use_errors=True
read background file 3c273_bg.pi

If Sherpa does not automatically read in the background data, then it can be input as follows:

sherpa> load_bkg("3c273_bg.pi")
statistical errors were found in file '3c273_bg.pi' 
but not used; to use them, re-read with use_errors=True

Defining the source and background instrument response

Since the header of the input source spectrum file referenced the instrument response files, an instrument response was automatically defined:

sherpa> print(get_arf())
name     = 3c273.arf
energ_lo = Float64[1090]
energ_hi = Float64[1090]
specresp = Float64[1090]
bin_lo   = None
bin_hi   = None
exposure = 38570.6924812

sherpa> print(get_rmf())
name     = 3c273.rmf
detchans = 1024
energ_lo = Float64[1090]
energ_hi = Float64[1090]
n_grp    = Int16[1090]
f_chan   = UInt32[2004]
n_chan   = UInt32[2004]
matrix   = Float64[58564]
offset   = 1
e_min    = Float64[1024]
e_max    = Float64[1024]

sherpa> print(get_bkg_arf());
name     = 3c273.arf
energ_lo = Float64[1090]
energ_hi = Float64[1090]
specresp = Float64[1090]
bin_lo   = None
bin_hi   = None
exposure = 38570.6924812

sherpa> print(get_bkg_rmf());
name     = 3c273.rmf
detchans = 1024
energ_lo = Float64[1090]
energ_hi = Float64[1090]
n_grp    = Int16[1090]
f_chan   = UInt32[2004]
n_chan   = UInt32[2004]
matrix   = Float64[58564]
offset   = 1
e_min    = Float64[1024]
e_max    = Float64[1024]

The instrument response was automatically set up for both the source and background. However, unless a background model is defined for fitting, using the set_bkg_model() command, then the background data will be ignored. In order to fit the background simultaneously with the source, the set_bkg_model() command will be issued in the next step. If you manually read in a background file after reading the source data, the background instrument response will be reset. To set it to be identical to the source instrument response, use load_bkg_arf() and load_bkg_rmf() with the source response files:

sherpa> load_bkg_rmf("3c273.rmf")
sherpa> load_bkg_arf("3c273.arf")

sherpa> print(get_bkg_arf());
name     = 3c273.arf
energ_lo = Float64[1090]
energ_hi = Float64[1090]
specresp = Float64[1090]
bin_lo   = None
bin_hi   = None
exposure = 38570.6924812

sherpa> print(get_bkg_rmf());
name     = 3c273.rmf
detchans = 1024
energ_lo = Float64[1090]
energ_hi = Float64[1090]
n_grp    = Int16[1090]
f_chan   = UInt32[2004]
n_chan   = UInt32[2004]
matrix   = Float64[58564]
offset   = 1
e_min    = Float64[1024]
e_max    = Float64[1024]

Defining and fitting source and background models

Here we simultaneously fit the source and background with models consisting of an absorbed power law. The source and background models are defined using the set_source and set_bkg_model() commands, respectively.

sherpa> set_source(xswabs.abs1*powlaw1d.srcp1)
sherpa> set_bkg_model(xswabs.abs1*powlaw1d.bkgp1)
sherpa> abs1.nh = 0.1
sherpa> guess(srcp1)
sherpa> guess(bkgp1)

sherpa> show_model()
Model: 1
apply_rmf(apply_arf((38564.6089269 * ((xswabs.abs1 * powlaw1d.srcp1) + scale_factor * ((xswabs.abs1 * powlaw1d.bkgp1))))))
   Param        Type          Value          Min          Max      Units
   -----        ----          -----          ---          ---      -----
   abs1.nh      thawed          0.1            0       100000 10^22 atoms / cm^2
   srcp1.gamma  thawed            1          -10           10           
   srcp1.ref    frozen            1 -3.40282e+38  3.40282e+38           
   srcp1.ampl   thawed  0.000144301  1.44301e-06    0.0144301           
   bkgp1.gamma  thawed            1          -10           10           
   bkgp1.ref    frozen            1 -3.40282e+38  3.40282e+38           
   bkgp1.ampl   thawed  0.000144301  1.44301e-06    0.0144301           

sherpa> print(get_bkg_model())
(38564.6089269 * (xswabs.abs1 * powlaw1d.bkgp1))
   Param        Type          Value          Min          Max      Units
   -----        ----          -----          ---          ---      -----
   abs1.nh      thawed          0.1            0       100000 10^22 atoms / cm^2
   bkgp1.gamma  thawed            1          -10           10           
   bkgp1.ref    frozen            1 -3.40282e+38  3.40282e+38           
   bkgp1.ampl   thawed  0.000144301  1.44301e-06    0.0144301  

sherpa> fit()
 Solar Abundance Vector set to angr:  Anders E. & Grevesse N. Geochimica et Cosmochimica Acta 53, 197 (1989)
 Cross Section Table set to bcmc:  Balucinska-Church and McCammon, 1998
Data Set               = 1
Method                = levmar
Statistic             = chi2gehrels
Initial fit statistic = 4604.01
Final fit statistic   = 137.257 at function evaluation 86
Data points           = 92
Degrees of freedom    = 87
Probability [Q-value] = 0.000478885
Reduced statistic     = 1.57766
Change in statistic   = 4466.75
   abs1.nh        0.0178327   
   srcp1.gamma    1.92493     
   srcp1.ampl     0.000174607 
   bkgp1.gamma    -0.167109   
   bkgp1.ampl     2.46666e-06 


sherpa> plot_fit_resid()

sherpa> log_scale(X_AXIS)
sherpa> log_scale(Y_AXIS)

sherpa> limits(Y_AXIS, 1.e-3, 0.2)
sherpa> limits(X_AXIS, 0.2, 3.5)

The results are shown Figure 3.

[Plot of the]
[Print media version: Plot of the]

Figure 3: Simultaneous fit of the source and background data (same responses)

Plot of the simultaneous fit of the 3c273 source and background spectra



Simultaneously Fit Source and Background with Independent Responses

Instead of subtracting the background, the user may choose to simultaneously fit the source and background spectra, each with its own RMF and ARF.

Reading FITS data for source and background

In this thread, we wish to fit spectral data from the FITS (ungrouped) datafiles source.pi and back.pi. These data sets are input into Sherpa with load commands:

sherpa> clean()

sherpa> load_pha("source.pi")
statistical errors were found in file 'source.pi' 
but not used; to use them, re-read with use_errors=True

sherpa> load_bkg("back.pi")
statistical errors were found in file 'source.pi' 
but not used; to use them, re-read with use_errors=True

Now the data sets can be plotted:

sherpa> plot_data()

sherpa> plot_bkg()

The plots are displayed in Figure 4. The data are plotted in bin (i.e. channel) space, since we have not yet defined the instrument response (RMFs and ARFs).


Defining source and background instrument responses

Here we establish 1-D instrument responses by loading the source and background response files with load commands:

sherpa> load_arf("source.arf")
sherpa> load_rmf("source.rmf")

sherpa> load_bkg_arf("back.arf")
sherpa> load_bkg_rmf("back.rmf")

The current definition of the instrument response may be examined using show_all() or get commands:

sherpa> print(get_arf())
name     = source.arf
energ_lo = Float64[1090]
energ_hi = Float64[1090]
specresp = Float64[1090]
bin_lo   = None
bin_hi   = None
exposure = 49470.9477594

sherpa> print(get_rmf())
name     = source.rmf
detchans = 1024
energ_lo = Float64[1090]
energ_hi = Float64[1090]
n_grp    = Int16[1090]
f_chan   = UInt32[1090]
n_chan   = UInt32[1090]
matrix   = Float64[572598]
offset   = 1
e_min    = Float64[1024]
e_max    = Float64[1024]

sherpa> print(get_bkg_arf())
name     = back.arf
energ_lo = Float64[1090]
energ_hi = Float64[1090]
specresp = Float64[1090]
bin_lo   = None
bin_hi   = None
exposure = 49470.9477594

sherpa> print(get_bkg_rmf())
name     = back.rmf
detchans = 1024
energ_lo = Float64[1090]
energ_hi = Float64[1090]
n_grp    = Int16[1090]
f_chan   = UInt32[1090]
n_chan   = UInt32[1090]
matrix   = Float64[552166]
offset   = 1
e_min    = Float64[1024]
e_max    = Float64[1024]

The output shows that source.arf and source.rmf currently define the source instrument response.


Defining and fitting source and background models

We now need to define a source model for our source data set with set_source and a background model for our background data set with set_bkg_model(). For these data, we find that absorbed blackbody spectra are appropriate. We need to define a model for the absorbing column and a blackbody for both the source and background:

sherpa> set_source(xswabs.a1*xsbbody.b1)
sherpa> set_bkg_model(a1*xsbbody.b2)
sherpa> ignore()
sherpa> notice(0.3, 10.)
sherpa> plot_fit()
sherpa> plot_bkg_fit()

Note that we also ignore the high and low energy regions of the spectrum, as they generally have lower quality data. The plots of the data with (default-value) fits produced by plot commands are shown in Figure 5 and Figure 6.

Clearly, the default values are not a very good fit to the data. We may choose to begin the fit now, using the fit() command, or we can refine the values of the fit by eye, prior to fitting. By varying parameter values, while plotting and replotting the data, we can help the fitting algorithm find the best minimum. By setting the parameters to values near what we expect, we also help avoid local minima in the parameter space. (Fitting spectra is something of an art; one generally gets better fits when they have a priori knowledge of the source.) We set the values of the parameters as follows:

sherpa> a1.nH=0.0336676
sherpa> b1.kT=20
sherpa> b1.norm=1e-02
sherpa> b2.kT=0.5
sherpa> b2.norm.min=1e-6
sherpa> b2.norm=1e-05

Note that the normalization of b2, "b2.norm", appears to have a better fit at values less than the default minimum. We set the b2.norm.min to a new, smaller value to give us the optimal fit. Now we have a reasonable start to the fit:

sherpa> plot_fit()
sherpa> plot_bkg_fit()

The plot is visible Figure 7.

In general, we may also want to change our fit statistic or method. Now, run the fit:

sherpa> fit()
Data Set               = 1
Method                = levmar
Statistic             = chi2gehrels
Initial fit statistic = 1024.04
Final fit statistic   = 945.974 at function evaluation 393
Data points           = 1330
Degrees of freedom    = 1325
Probability [Q-value] = 1
Reduced statistic     = 0.713942
Change in statistic   = 78.0655
   a1.nh          0.0336454   
   b1.kt          100         
   b1.norm        1.07253     
   b2.kt          0.56523     
   b2.norm        1.16969e-05 

Once the fit is complete, Sherpa will display the fit values to the screen. You may also display the overall status of the Sherpa session with the show_all() command:

sherpa> show_all()
Data Set: 1
Filter: 0.2993-9.9937 Energy (keV)
Noticed Channels: 21.0-685.0
name           = source.pi
channel        = Float64[1024]
counts         = Float64[1024]
staterror      = None
syserror       = None
bin_lo         = None
bin_hi         = None
grouping       = None
quality        = None
exposure       = 49429.2334679
backscal       = 1.87253514146e-05
areascal       = 1.0
grouped        = False
subtracted     = False
units          = energy
response_ids   = [1]
background_ids = [1]

RMF Data Set: 1:1
name     = source.rmf
detchans = 1024
energ_lo = Float64[1090]
energ_hi = Float64[1090]
n_grp    = Int16[1090]
f_chan   = UInt32[1090]
n_chan   = UInt32[1090]
matrix   = Float64[572598]
offset   = 1
e_min    = Float64[1024]
e_max    = Float64[1024]

ARF Data Set: 1:1
name     = source.arf
energ_lo = Float64[1090]
energ_hi = Float64[1090]
specresp = Float64[1090]
bin_lo   = None
bin_hi   = None
exposure = 49470.9477594

Background Data Set: 1:1
name           = back.pi
channel        = Float64[1024]
counts         = Float64[1024]
staterror      = None
syserror       = None
bin_lo         = None
bin_hi         = None
grouping       = None
quality        = None
exposure       = 49429.2334679
backscal       = 2.69645060371e-05
areascal       = 1.0
grouped        = False
subtracted     = False
units          = energy
response_ids   = [1]
background_ids = []

Background RMF Data Set: 1:1
name     = back.rmf
detchans = 1024
energ_lo = Float64[1090]
energ_hi = Float64[1090]
n_grp    = Int16[1090]
f_chan   = UInt32[1090]
n_chan   = UInt32[1090]
matrix   = Float64[552166]
offset   = 1
e_min    = Float64[1024]
e_max    = Float64[1024]

Background ARF Data Set: 1:1
name     = back.arf
energ_lo = Float64[1090]
energ_hi = Float64[1090]
specresp = Float64[1090]
bin_lo   = None
bin_hi   = None
exposure = 49470.9477594

Model: 1
apply_rmf(apply_arf((49429.2334679 * ((xswabs.a1 * xsbbody.b1) + scale_factor * ((xswabs.a1 * xsbbody.b2))))))
   Param        Type          Value          Min          Max      Units
   -----        ----          -----          ---          ---      -----
   a1.nh        thawed    0.0336454            0       100000 10^22 atoms / cm^2
   b1.kt        thawed          100         0.01          100        keV
   b1.norm      thawed      1.07253            0        1e+24 L39 / (D10)**2
   b2.kt        thawed      0.56523         0.01          100        keV
   b2.norm      thawed  1.16969e-05        1e-06        1e+24 L39 / (D10)**2

Optimization Method: LevMar
name    = levmar
ftol    = 1.19209289551e-07
xtol    = 1.19209289551e-07
gtol    = 1.19209289551e-07
maxfev  = None
epsfcn  = 1.19209289551e-07
factor  = 100.0
verbose = 0

Statistic: Chi2Gehrels

Fit:Data Set               = 1
Method                = levmar
Statistic             = chi2gehrels
Initial fit statistic = 1024.04
Final fit statistic   = 945.974 at function evaluation 393
Data points           = 1330
Degrees of freedom    = 1325
Probability [Q-value] = 1
Reduced statistic     = 0.713942
Change in statistic   = 78.0655
   a1.nh          0.0336454   
   b1.kt          100         
   b1.norm        1.07253     
   b2.kt          0.56523     
   b2.norm        1.16969e-05 

One may visually examine the fit as well:

sherpa> plot_fit()
sherpa> plot_bkg_fit()

The plots are visible in Figure 9 and Figure 10.



Scripting It

The file fit.py is a Python script which performs the primary commands used above; it can be executed by typing execfile("fit.py") on the Sherpa command line.

The Sherpa script command may be used to save everything typed on the command line in a Sherpa session:

sherpa> script(filename="sherpa.log", clobber=False)

The CXC is committed to helping Sherpa users transition to new syntax as smoothly as possible. If you have existing Sherpa scripts or save files, submit them to us via the CXC Helpdesk and we will provide the CIAO/Sherpa 4.1 syntax to you.




Summary

This thread is complete, so we can exit the Sherpa session:

sherpa> quit


History

14 Jan 2005 updated for CIAO 3.2: minor change in fit results
21 Dec 2005 reviewed for CIAO 3.3: no changes
01 Dec 2006 reviewed for CIAO 3.4: no changes
08 Dec 2008 updated for Sherpa 4.1
29 Apr 2009 new script command is available with CIAO 4.1.2

Return to Threads Page: Top | All | Fitting
Hardcopy (PDF): A4 | Letter
Last modified: 29 April 2009


The Chandra X-Ray Center (CXC) is operated for NASA by the Smithsonian Astrophysical Observatory.
60 Garden Street, Cambridge, MA 02138 USA.    Email: cxcweb@head.cfa.harvard.edu
Smithsonian Institution, Copyright © 1998-2004. All rights reserved.