Exemplo n.º 1
0
def test_simulate_bkgnd_spectrum():
    tmpdir = tempfile.mkdtemp()
    curdir = os.getcwd()
    os.chdir(tmpdir)

    prng = RandomState(29)

    hdxi_arf = AuxiliaryResponseFile("xrs_hdxi_3x10.arf")
    hdxi_rmf = RedistributionMatrixFile("xrs_hdxi.rmf")

    exp_time = 50000.0
    fov = 3600.0
    simulate_spectrum(None, "hdxi", exp_time, "test_bkgnd.pha",
                      instr_bkgnd=True, foreground=True, prng=prng,
                      overwrite=True, bkgnd_area=(fov, "arcsec**2"))
    ch_min = hdxi_rmf.e_to_ch(0.7)-hdxi_rmf.cmin
    ch_max = hdxi_rmf.e_to_ch(2.0)-hdxi_rmf.cmin
    f = pyfits.open("test_bkgnd.pha")
    ncts = f["SPECTRUM"].data["COUNTS"][ch_min:ch_max].sum()
    f.close()
    S = ncts/exp_time/fov
    dS = np.sqrt(ncts)/exp_time/fov
    foreground = ConvolvedBackgroundSpectrum(hm_astro_bkgnd, hdxi_arf)
    f_sum = foreground.get_flux_in_band(0.7, 2.0)[0]
    i_sum = acisi_particle_bkgnd.get_flux_in_band(0.7, 2.0)[0]
    b_sum = (f_sum+i_sum).to("ph/(arcsec**2*s)").value
    assert np.abs(S-b_sum) < 1.645*dS

    os.chdir(curdir)
    shutil.rmtree(tmpdir)
Exemplo n.º 2
0
def write_spectrum(evtfile, specfile, overwrite=False):
    r"""
    Bin event energies into a spectrum and write it to 
    a FITS binary table. Does not do any grouping of 
    channels, and will automatically determine PI or PHA. 

    Parameters
    ----------
    evtfile : string
        The name of the event file to read the events from. 
    specfile : string
        The name of the spectrum file to be written.
    overwrite : boolean, optional
        Whether or not to overwrite an existing file with 
        the same name. Default: False
    """
    from soxs.instrument import RedistributionMatrixFile
    parameters = {}
    if isinstance(evtfile, str):
        f = pyfits.open(evtfile)
        spectype = f["EVENTS"].header["CHANTYPE"]
        rmf = f["EVENTS"].header["RESPFILE"]
        p = f["EVENTS"].data[spectype]
        exp_time = f["EVENTS"].header["EXPOSURE"]
        for key in ["RESPFILE", "ANCRFILE", "MISSION", "TELESCOP", "INSTRUME"]:
            parameters[key] = f["EVENTS"].header[key]
        f.close()
    else:
        rmf = evtfile["rmf"]
        spectype = evtfile["channel_type"]
        p = evtfile[spectype]
        parameters["RESPFILE"] = os.path.split(rmf)[-1]
        parameters["ANCRFILE"] = os.path.split(evtfile["arf"])[-1]
        parameters["TELESCOP"] = evtfile["telescope"]
        parameters["INSTRUME"] = evtfile["instrument"]
        parameters["MISSION"] = evtfile["mission"]
        exp_time = evtfile["exposure_time"]

    rmf = RedistributionMatrixFile(rmf)
    minlength = rmf.n_ch
    if rmf.cmin == 1:
        minlength += 1
    spec = np.bincount(p, minlength=minlength)
    if rmf.cmin == 1:
        spec = spec[1:]
    bins = (np.arange(rmf.n_ch) + rmf.cmin).astype("int32")

    _write_spectrum(bins,
                    spec,
                    exp_time,
                    spectype,
                    parameters,
                    specfile,
                    overwrite=overwrite)
Exemplo n.º 3
0
def test_simulate_bkgnd_spectrum():
    tmpdir = tempfile.mkdtemp()
    curdir = os.getcwd()
    os.chdir(tmpdir)

    prng = RandomState(29)

    hdxi_arf = AuxiliaryResponseFile("xrs_hdxi_3x10.arf")
    hdxi_rmf = RedistributionMatrixFile("xrs_hdxi.rmf")

    exp_time = 50000.0
    fov = 3600.0
    simulate_spectrum(None,
                      "hdxi",
                      exp_time,
                      "test_bkgnd.pha",
                      instr_bkgnd=True,
                      foreground=True,
                      prng=prng,
                      overwrite=True,
                      bkgnd_area=(fov, "arcsec**2"))
    ch_min = hdxi_rmf.e_to_ch(0.7) - hdxi_rmf.cmin
    ch_max = hdxi_rmf.e_to_ch(2.0) - hdxi_rmf.cmin
    f = pyfits.open("test_bkgnd.pha")
    ncts = f["SPECTRUM"].data["COUNTS"][ch_min:ch_max].sum()
    f.close()
    S = ncts / exp_time / fov
    dS = np.sqrt(ncts) / exp_time / fov
    foreground = ConvolvedBackgroundSpectrum(hm_astro_bkgnd, hdxi_arf)
    f_sum = foreground.get_flux_in_band(0.7, 2.0)[0]
    i_sum = acisi_particle_bkgnd.get_flux_in_band(0.7, 2.0)[0]
    b_sum = (f_sum + i_sum).to("ph/(arcsec**2*s)").value
    assert np.abs(S - b_sum) < 1.645 * dS

    os.chdir(curdir)
    shutil.rmtree(tmpdir)
Exemplo n.º 4
0
from soxs.spectra import ApecGenerator
from soxs.spatial import PointSourceModel
from soxs.simput import SimputCatalog
from soxs.instrument_registry import \
    get_instrument_from_registry
from soxs.instrument import instrument_simulator, \
    RedistributionMatrixFile, simulate_spectrum
from soxs.events import write_spectrum
from numpy.random import RandomState
from numpy.testing import assert_allclose
from soxs.tests.utils import spectrum_answer_testing, \
    file_answer_testing

inst_name = "lynx_lxm"

rmf = RedistributionMatrixFile.from_instrument(inst_name)
agen0 = ApecGenerator(0.01, 10.0, 20000, broadening=True)
agen_var0 = ApecGenerator(0.01, 10.0, 20000, var_elem=["O", "Fe"],
                          broadening=True)
agen_nolines0 = ApecGenerator(0.01, 10.0, 20000, broadening=True,
                              nolines=True)
agen_aspl0 = ApecGenerator(0.01, 10.0, 20000, broadening=True,
                           abund_table="aspl")
agen = ApecGenerator(rmf.elo[0], rmf.ehi[-1], rmf.n_e, broadening=True)
agen_var = ApecGenerator(rmf.elo[0], rmf.ehi[-1], rmf.n_e,
                         var_elem=["O", "Fe"], broadening=True)
agen_nolines = ApecGenerator(rmf.elo[0], rmf.ehi[-1], rmf.n_e,
                             broadening=True, nolines=True)
agen_aspl = ApecGenerator(rmf.elo[0], rmf.ehi[-1], rmf.n_e,
                          broadening=True, abund_table="aspl")
agen_nei = ApecGenerator(rmf.elo[0], rmf.ehi[-1], rmf.n_e,
Exemplo n.º 5
0
    AuxiliaryResponseFile, instrument_simulator
from soxs.events import write_spectrum
from soxs.instrument_registry import get_instrument_from_registry

ckms = clight.in_units("km/s").v

def setup():
    from yt.config import ytcfg
    ytcfg["yt", "__withintesting"] = "True"

try:
    mucal_spec = get_instrument_from_registry("mucal")
except KeyError:
    pass

rmf = RedistributionMatrixFile(mucal_spec["rmf"])
arf = AuxiliaryResponseFile(mucal_spec['arf'])
fit_model = TableApecModel(rmf.elo[0], rmf.ehi[-1], rmf.n_e)
agen_var = TableApecModel(rmf.elo[0], rmf.ehi[-1], rmf.n_e,
                          var_elem=["O", "Ca"], thermal_broad=True)


def mymodel(pars, x, xhi=None):
    dx = x[1]-x[0]
    tm = TBabsModel(pars[0])
    tbabs = tm.get_absorb(x+0.5*dx)
    bapec = fit_model.return_spectrum(pars[1], pars[2], pars[3], pars[4], velocity=pars[5])
    eidxs = np.logical_and(rmf.elo >= x[0]-0.5*dx, rmf.elo <= x[-1]+0.5*dx)
    return tbabs*bapec[eidxs]

Exemplo n.º 6
0
def plaw_fit(alpha_sim):

    tmpdir = tempfile.mkdtemp()
    curdir = os.getcwd()
    os.chdir(tmpdir)

    nH_sim = 0.02
    norm_sim = 1.0e-4
    redshift = 0.01

    exp_time = 5.0e4
    area = 40000.0
    inst_name = "hdxi"

    spec = Spectrum.from_powerlaw(alpha_sim, redshift, norm_sim)
    spec.apply_foreground_absorption(nH_sim)
    e = spec.generate_energies(exp_time, area)

    pt_src = PointSourceModel(30.0, 45.0, e.size)

    write_photon_list("plaw_model",
                      "plaw_model",
                      e.flux,
                      pt_src.ra,
                      pt_src.dec,
                      e,
                      clobber=True)

    instrument_simulator("plaw_model_simput.fits",
                         "plaw_model_evt.fits",
                         exp_time,
                         inst_name, [30.0, 45.0],
                         astro_bkgnd=None,
                         instr_bkgnd_scale=0.0)

    inst = get_instrument_from_registry(inst_name)
    arf = AuxiliaryResponseFile(inst["arf"])
    rmf = RedistributionMatrixFile(inst["rmf"])
    os.system("cp %s ." % arf.filename)
    os.system("cp %s ." % rmf.filename)

    write_spectrum("plaw_model_evt.fits", "plaw_model_evt.pha", clobber=True)

    load_user_model(mymodel, "wplaw")
    add_user_pars("wplaw", ["nH", "norm", "redshift", "alpha"],
                  [0.01, norm_sim * 0.8, redshift, 0.9],
                  parmins=[0.0, 0.0, 0.0, 0.1],
                  parmaxs=[10.0, 1.0e9, 10.0, 10.0],
                  parfrozen=[False, False, True, False])

    load_pha("plaw_model_evt.pha")
    set_stat("cstat")
    set_method("simplex")
    ignore(":0.5, 9.0:")
    set_model("wplaw")
    fit()
    set_covar_opt("sigma", 1.645)
    covar()
    res = get_covar_results()

    assert np.abs(res.parvals[0] - nH_sim) < res.parmaxes[0]
    assert np.abs(res.parvals[1] - norm_sim) < res.parmaxes[1]
    assert np.abs(res.parvals[2] - alpha_sim) < res.parmaxes[2]

    os.chdir(curdir)
    shutil.rmtree(tmpdir)
Exemplo n.º 7
0
def write_spectrum(evtfile, specfile, clobber=False):
    r"""
    Bin event energies into a spectrum and write it to a FITS binary table. Can bin
    on energy or channel. In the latter case, the spectral binning will be determined by
    the RMF binning.

    Parameters
    ----------
    specfile : string
        The name of the FITS file to be written.
    bin_type : string, optional
        Bin on "energy" or "channel". If an RMF is detected, channel information will be
        imported from it. 
    emin : float, optional
        The minimum energy of the spectral bins in keV. Only used if binning without an RMF.
    emax : float, optional
        The maximum energy of the spectral bins in keV. Only used if binning without an RMF.
    nchan : integer, optional
        The number of channels. Only used if binning without an RMF.
    """
    f = pyfits.open(evtfile)
    spectype = f["EVENTS"].header["CHANTYPE"]
    rmf = RedistributionMatrixFile(f["EVENTS"].header["RESPFILE"])
    minlength = rmf.n_ch
    if rmf.cmin == 1: minlength += 1
    spec = np.bincount(f["EVENTS"].data[spectype], minlength=minlength)
    if rmf.cmin == 1: spec = spec[1:]
    bins = (np.arange(rmf.n_ch) + rmf.cmin).astype("int32")

    col1 = pyfits.Column(name='CHANNEL', format='1J', array=bins)
    col2 = pyfits.Column(name=spectype.upper(),
                         format='1D',
                         array=bins.astype("float64"))
    col3 = pyfits.Column(name='COUNTS',
                         format='1J',
                         array=spec.astype("int32"))
    col4 = pyfits.Column(name='COUNT_RATE',
                         format='1D',
                         array=spec / f["EVENTS"].header["EXPOSURE"])

    coldefs = pyfits.ColDefs([col1, col2, col3, col4])

    tbhdu = pyfits.BinTableHDU.from_columns(coldefs)
    tbhdu.update_ext_name("SPECTRUM")

    tbhdu.header["DETCHANS"] = spec.size
    tbhdu.header["TOTCTS"] = spec.sum()
    tbhdu.header["EXPOSURE"] = f["EVENTS"].header["EXPOSURE"]
    tbhdu.header["LIVETIME"] = f["EVENTS"].header["EXPOSURE"]
    tbhdu.header["CONTENT"] = spectype
    tbhdu.header["HDUCLASS"] = "OGIP"
    tbhdu.header["HDUCLAS1"] = "SPECTRUM"
    tbhdu.header["HDUCLAS2"] = "TOTAL"
    tbhdu.header["HDUCLAS3"] = "TYPE:I"
    tbhdu.header["HDUCLAS4"] = "COUNT"
    tbhdu.header["HDUVERS"] = "1.1.0"
    tbhdu.header["HDUVERS1"] = "1.1.0"
    tbhdu.header["CHANTYPE"] = spectype
    tbhdu.header["BACKFILE"] = "none"
    tbhdu.header["CORRFILE"] = "none"
    tbhdu.header["POISSERR"] = True
    for key in ["RESPFILE", "ANCRFILE", "MISSION", "TELESCOP", "INSTRUME"]:
        tbhdu.header[key] = f["EVENTS"].header[key]
    tbhdu.header["AREASCAL"] = 1.0
    tbhdu.header["CORRSCAL"] = 0.0
    tbhdu.header["BACKSCAL"] = 1.0

    f.close()

    hdulist = pyfits.HDUList([pyfits.PrimaryHDU(), tbhdu])

    hdulist.writeto(specfile, clobber=clobber)
Exemplo n.º 8
0
    make_simple_instrument


def setup():
    from yt.config import ytcfg
    ytcfg["yt", "__withintesting"] = "True"


try:
    make_simple_instrument("acisi_cy19", "sq_acisi_cy19", 20.0, 2400)
except KeyError:
    pass

acis_spec = get_instrument_from_registry("sq_acisi_cy19")

rmf = RedistributionMatrixFile(acis_spec["rmf"])
arf = AuxiliaryResponseFile(acis_spec['arf'])


def mymodel(pars, x, xhi=None):
    dx = x[1] - x[0]
    xmid = x + 0.5 * dx
    wm = WabsModel(pars[0])
    wabs = wm.get_absorb(xmid)
    plaw = pars[1] * dx * (xmid * (1.0 + pars[2]))**(-pars[3])
    return wabs * plaw


@requires_module("sherpa")
def test_power_law():
    plaw_fit(1.1, prng=29)
Exemplo n.º 9
0
    def write_fits_file(self, fitsfile, fov, nx, overwrite=False):
        """
        Write events to a FITS binary table file. The result is a
        standard "event file" which can be processed by standard
        X-ray analysis tools.

        Parameters
        ----------
        fitsfile : string
            The name of the event file to write.
        fov : float, (value, unit) tuple, :class:`~yt.units.yt_array.YTQuantity`, or :class:`~astropy.units.Quantity`
            The field of view of the event file. If units are not 
            provided, they are assumed to be in arcminutes.
        nx : integer
            The resolution of the image (number of pixels on a side). 
        overwrite : boolean, optional
            Set to True to overwrite a previous file.
        """
        from astropy.time import Time, TimeDelta

        events = communicate_events(self.events)

        fov = parse_value(fov, "arcmin")

        if comm.rank == 0:

            exp_time = float(self.parameters["exp_time"])

            t_begin = Time.now()
            dt = TimeDelta(exp_time, format='sec')
            t_end = t_begin + dt

            dtheta = fov.to("deg").v / nx

            wcs = pywcs.WCS(naxis=2)
            wcs.wcs.crpix = [0.5 * (nx + 1)] * 2
            wcs.wcs.crval = self.parameters["sky_center"].d
            wcs.wcs.cdelt = [-dtheta, dtheta]
            wcs.wcs.ctype = ["RA---TAN", "DEC--TAN"]
            wcs.wcs.cunit = ["deg"] * 2

            xx, yy = wcs.wcs_world2pix(self["xsky"].d, self["ysky"].d, 1)

            keepx = np.logical_and(xx >= 0.5, xx <= float(nx) + 0.5)
            keepy = np.logical_and(yy >= 0.5, yy <= float(nx) + 0.5)
            keep = np.logical_and(keepx, keepy)

            n_events = keep.sum()

            mylog.info("Threw out %d events because " % (xx.size - n_events) +
                       "they fell outside the field of view.")

            col_e = pyfits.Column(name='ENERGY',
                                  format='E',
                                  unit='eV',
                                  array=events["eobs"].in_units("eV").d[keep])
            col_x = pyfits.Column(name='X',
                                  format='D',
                                  unit='pixel',
                                  array=xx[keep])
            col_y = pyfits.Column(name='Y',
                                  format='D',
                                  unit='pixel',
                                  array=yy[keep])

            cols = [col_e, col_x, col_y]

            if "channel_type" in self.parameters:
                chantype = self.parameters["channel_type"]
                if chantype == "pha":
                    cunit = "adu"
                elif chantype == "pi":
                    cunit = "Chan"
                col_ch = pyfits.Column(name=chantype.upper(),
                                       format='1J',
                                       unit=cunit,
                                       array=events[chantype][keep])
                cols.append(col_ch)

                time = np.random.uniform(size=n_events,
                                         low=0.0,
                                         high=float(
                                             self.parameters["exp_time"]))
                col_t = pyfits.Column(name="TIME",
                                      format='1D',
                                      unit='s',
                                      array=time)
                cols.append(col_t)

            coldefs = pyfits.ColDefs(cols)
            tbhdu = pyfits.BinTableHDU.from_columns(coldefs)
            tbhdu.name = "EVENTS"

            tbhdu.header["MTYPE1"] = "sky"
            tbhdu.header["MFORM1"] = "x,y"
            tbhdu.header["MTYPE2"] = "EQPOS"
            tbhdu.header["MFORM2"] = "RA,DEC"
            tbhdu.header["TCTYP2"] = "RA---TAN"
            tbhdu.header["TCTYP3"] = "DEC--TAN"
            tbhdu.header["TCRVL2"] = float(self.parameters["sky_center"][0])
            tbhdu.header["TCRVL3"] = float(self.parameters["sky_center"][1])
            tbhdu.header["TCDLT2"] = -dtheta
            tbhdu.header["TCDLT3"] = dtheta
            tbhdu.header["TCRPX2"] = 0.5 * (nx + 1)
            tbhdu.header["TCRPX3"] = 0.5 * (nx + 1)
            tbhdu.header["TLMIN2"] = 0.5
            tbhdu.header["TLMIN3"] = 0.5
            tbhdu.header["TLMAX2"] = float(nx) + 0.5
            tbhdu.header["TLMAX3"] = float(nx) + 0.5
            if "channel_type" in self.parameters:
                rmf = RedistributionMatrixFile(self.parameters["rmf"])
                tbhdu.header["TLMIN4"] = rmf.cmin
                tbhdu.header["TLMAX4"] = rmf.cmax
                tbhdu.header["RESPFILE"] = os.path.split(
                    self.parameters["rmf"])[-1]
                tbhdu.header["PHA_BINS"] = rmf.n_ch
                tbhdu.header["ANCRFILE"] = os.path.split(
                    self.parameters["arf"])[-1]
                tbhdu.header["CHANTYPE"] = self.parameters["channel_type"]
                tbhdu.header["MISSION"] = self.parameters["mission"]
                tbhdu.header["TELESCOP"] = self.parameters["telescope"]
                tbhdu.header["INSTRUME"] = self.parameters["instrument"]
            tbhdu.header["EXPOSURE"] = exp_time
            tbhdu.header["TSTART"] = 0.0
            tbhdu.header["TSTOP"] = exp_time
            tbhdu.header["AREA"] = float(self.parameters["area"])
            tbhdu.header["HDUVERS"] = "1.1.0"
            tbhdu.header["RADECSYS"] = "FK5"
            tbhdu.header["EQUINOX"] = 2000.0
            tbhdu.header["HDUCLASS"] = "OGIP"
            tbhdu.header["HDUCLAS1"] = "EVENTS"
            tbhdu.header["HDUCLAS2"] = "ACCEPTED"
            tbhdu.header["DATE"] = t_begin.tt.isot
            tbhdu.header["DATE-OBS"] = t_begin.tt.isot
            tbhdu.header["DATE-END"] = t_end.tt.isot

            hdulist = [pyfits.PrimaryHDU(), tbhdu]

            if "channel_type" in self.parameters:
                start = pyfits.Column(name='START',
                                      format='1D',
                                      unit='s',
                                      array=np.array([0.0]))
                stop = pyfits.Column(name='STOP',
                                     format='1D',
                                     unit='s',
                                     array=np.array([exp_time]))

                tbhdu_gti = pyfits.BinTableHDU.from_columns([start, stop])
                tbhdu_gti.name = "STDGTI"
                tbhdu_gti.header["TSTART"] = 0.0
                tbhdu_gti.header["TSTOP"] = exp_time
                tbhdu_gti.header["HDUCLASS"] = "OGIP"
                tbhdu_gti.header["HDUCLAS1"] = "GTI"
                tbhdu_gti.header["HDUCLAS2"] = "STANDARD"
                tbhdu_gti.header["RADECSYS"] = "FK5"
                tbhdu_gti.header["EQUINOX"] = 2000.0
                tbhdu_gti.header["DATE"] = t_begin.tt.isot
                tbhdu_gti.header["DATE-OBS"] = t_begin.tt.isot
                tbhdu_gti.header["DATE-END"] = t_end.tt.isot

                hdulist.append(tbhdu_gti)

            pyfits.HDUList(hdulist).writeto(fitsfile, overwrite=overwrite)

        comm.barrier()
Exemplo n.º 10
0
def make_background(exp_time, instrument, sky_center, foreground=True, 
                    ptsrc_bkgnd=True, instr_bkgnd=True, no_dither=False,
                    dither_params=None, roll_angle=0.0, subpixel_res=False, 
                    input_sources=None, absorb_model="wabs", nH=0.05, prng=None):
    """
    Make background events. 

    Parameters
    ----------
    exp_time : float, (value, unit) tuple, or :class:`~astropy.units.Quantity`
        The exposure time to use, in seconds. 
    instrument : string
        The name of the instrument to use, which picks an instrument
        specification from the instrument registry. 
    sky_center : array, tuple, or list
        The center RA, Dec coordinates of the observation, in degrees.
    foreground : boolean, optional
        Whether or not to include the Galactic foreground. Default: True
    instr_bkgnd : boolean, optional
        Whether or not to include the instrumental background. Default: True
    no_dither : boolean, optional
        If True, turn off dithering entirely. Default: False
    dither_params : array-like of floats, optional
        The parameters to use to control the size and period of the dither
        pattern. The first two numbers are the dither amplitude in x and y
        detector coordinates in arcseconds, and the second two numbers are
        the dither period in x and y detector coordinates in seconds. 
        Default: [8.0, 8.0, 1000.0, 707.0].
    ptsrc_bkgnd : boolean, optional
        Whether or not to include the point-source background. Default: True
        Default: 0.05
    roll_angle : float, (value, unit) tuple, or :class:`~astropy.units.Quantity`, optional
        The roll angle of the observation in degrees. Default: 0.0
    subpixel_res: boolean, optional
        If True, event positions are not randomized within the pixels 
        within which they are detected. Default: False
    input_sources : string, optional
        If set to a filename, input the point source positions, fluxes,
        and spectral indices from an ASCII table instead of generating
        them. Default: None
    absorb_model : string, optional
        The absorption model to use, "wabs" or "tbabs". Default: "wabs"
    nH : float, optional
        The hydrogen column in units of 10**22 atoms/cm**2. 
        Default: 0.05
    prng : :class:`~numpy.random.RandomState` object, integer, or None
        A pseudo-random number generator. Typically will only 
        be specified if you have a reason to generate the same 
        set of random numbers, such as for a test. Default is None, 
        which sets the seed based on the system time. 
    """
    from soxs.background import make_instrument_background, \
        make_foreground, make_ptsrc_background
    prng = parse_prng(prng)
    exp_time = parse_value(exp_time, "s")
    roll_angle = parse_value(roll_angle, "deg")
    try:
        instrument_spec = instrument_registry[instrument]
    except KeyError:
        raise KeyError("Instrument %s is not in the instrument registry!" % instrument)
    if not instrument_spec["imaging"]:
        raise RuntimeError("Instrument '%s' is not " % instrument_spec["name"] +
                           "designed for imaging observations!")
    fov = instrument_spec["fov"]

    input_events = defaultdict(list)

    arf_file = get_response_path(instrument_spec["arf"])
    arf = AuxiliaryResponseFile(arf_file)
    rmf_file = get_response_path(instrument_spec["rmf"])
    rmf = RedistributionMatrixFile(rmf_file)

    if ptsrc_bkgnd:
        mylog.info("Adding in point-source background.")
        ptsrc_events = make_ptsrc_background(exp_time, fov, sky_center,
                                             area=1.2*arf.max_area,
                                             input_sources=input_sources, 
                                             absorb_model=absorb_model,
                                             nH=nH, prng=prng)
        for key in ["ra", "dec", "energy"]:
            input_events[key].append(ptsrc_events[key])
        input_events["flux"].append(ptsrc_events["flux"])
        input_events["emin"].append(ptsrc_events["energy"].min())
        input_events["emax"].append(ptsrc_events["energy"].max())
        input_events["sources"].append("ptsrc_bkgnd")
        events, event_params = generate_events(input_events, exp_time,
                                               instrument, sky_center,
                                               no_dither=no_dither,
                                               dither_params=dither_params, 
                                               roll_angle=roll_angle,
                                               subpixel_res=subpixel_res,
                                               prng=prng)
        mylog.info("Generated %d photons from the point-source background." % len(events["energy"]))
    else:
        nx = instrument_spec["num_pixels"]
        events = defaultdict(list)
        if not instrument_spec["dither"]:
            dither_on = False
        else:
            dither_on = not no_dither
        if dither_params is None:
            dither_params = [8.0, 8.0, 1000.0, 707.0]
        dither_dict = {"x_amp": dither_params[0],
                       "y_amp": dither_params[1],
                       "x_period": dither_params[2],
                       "y_period": dither_params[3],
                       "dither_on": dither_on,
                       "plate_scale": instrument_spec["fov"]/nx*60.0}
        event_params = {"exposure_time": exp_time, 
                        "fov": instrument_spec["fov"],
                        "num_pixels": nx,
                        "pix_center": np.array([0.5*(2*nx+1)]*2),
                        "channel_type": rmf.header["CHANTYPE"],
                        "sky_center": sky_center,
                        "dither_params": dither_dict,
                        "plate_scale": instrument_spec["fov"]/nx/60.0,
                        "chan_lim": [rmf.cmin, rmf.cmax],
                        "rmf": rmf_file, "arf": arf_file,
                        "telescope": rmf.header["TELESCOP"],
                        "instrument": instrument_spec['name'],
                        "mission": rmf.header.get("MISSION", ""),
                        "nchan": rmf.n_ch,
                        "roll_angle": roll_angle,
                        "aimpt_coords": instrument_spec["aimpt_coords"]}

    if "chips" not in event_params:
        event_params["chips"] = instrument_spec["chips"]

    if foreground:
        mylog.info("Adding in astrophysical foreground.")
        bkg_events = make_foreground(event_params, arf, rmf, prng=prng)
        for key in bkg_events:
            events[key] = np.concatenate([events[key], bkg_events[key]])
    if instr_bkgnd and instrument_spec["bkgnd"] is not None:
        mylog.info("Adding in instrumental background.")
        bkg_events = make_instrument_background(instrument_spec["bkgnd"], 
                                                event_params, rmf, prng=prng)
        for key in bkg_events:
            events[key] = np.concatenate([events[key], bkg_events[key]])

    return events, event_params
Exemplo n.º 11
0
def generate_events(input_events, exp_time, instrument, sky_center, 
                    no_dither=False, dither_params=None, 
                    roll_angle=0.0, subpixel_res=False, prng=None):
    """
    Take unconvolved events and convolve them with instrumental responses. This 
    function does the following:

    1. Determines which events are observed using the ARF
    2. Pixelizes the events, applying PSF effects and dithering
    3. Determines energy channels using the RMF

    This function is not meant to be called by the end-user but is used by
    the :func:`~soxs.instrument.instrument_simulator` function.

    Parameters
    ----------
    input_events : string, dict, or None
        The unconvolved events to be used as input. Can be one of the
        following:
        1. The name of a SIMPUT catalog file.
        2. A Python dictionary containing the following items:
        "ra": A NumPy array of right ascension values in degrees.
        "dec": A NumPy array of declination values in degrees.
        "energy": A NumPy array of energy values in keV.
        "flux": The flux of the entire source, in units of erg/cm**2/s.
    out_file : string
        The name of the event file to be written.
    exp_time : float, (value, unit) tuple, or :class:`~astropy.units.Quantity`
        The exposure time to use, in seconds. 
    instrument : string
        The name of the instrument to use, which picks an instrument
        specification from the instrument registry. 
    sky_center : array, tuple, or list
        The center RA, Dec coordinates of the observation, in degrees.
    no_dither : boolean, optional
        If True, turn off dithering entirely. Default: False
    dither_params : array-like of floats, optional
        The parameters to use to control the size and period of the dither
        pattern. The first two numbers are the dither amplitude in x and y
        detector coordinates in arcseconds, and the second two numbers are
        the dither period in x and y detector coordinates in seconds. 
        Default: [8.0, 8.0, 1000.0, 707.0].
    roll_angle : float, (value, unit) tuple, or :class:`~astropy.units.Quantity`, optional
        The roll angle of the observation in degrees. Default: 0.0
    subpixel_res: boolean, optional
        If True, event positions are not randomized within the pixels 
        within which they are detected. Default: False
    prng : :class:`~numpy.random.RandomState` object, integer, or None
        A pseudo-random number generator. Typically will only 
        be specified if you have a reason to generate the same 
        set of random numbers, such as for a test. Default is None, 
        which sets the seed based on the system time. 
    """
    import pyregion._region_filter as rfilter
    exp_time = parse_value(exp_time, "s")
    roll_angle = parse_value(roll_angle, "deg")
    prng = parse_prng(prng)
    if isinstance(input_events, dict):
        parameters = {}
        for key in ["flux", "emin", "emax", "sources"]:
            parameters[key] = input_events[key]
        event_list = []
        for i in range(len(parameters["flux"])):
            edict = {}
            for key in ["ra", "dec", "energy"]:
                edict[key] = input_events[key][i]
            event_list.append(edict)
    elif isinstance(input_events, string_types):
        # Assume this is a SIMPUT catalog
        event_list, parameters = read_simput_catalog(input_events)

    try:
        instrument_spec = instrument_registry[instrument]
    except KeyError:
        raise KeyError("Instrument %s is not in the instrument registry!" % instrument)
    if not instrument_spec["imaging"]:
        raise RuntimeError("Instrument '%s' is not " % instrument_spec["name"] +
                           "designed for imaging observations!")

    arf_file = get_response_path(instrument_spec["arf"])
    rmf_file = get_response_path(instrument_spec["rmf"])
    arf = AuxiliaryResponseFile(arf_file)
    rmf = RedistributionMatrixFile(rmf_file)

    nx = instrument_spec["num_pixels"]
    plate_scale = instrument_spec["fov"]/nx/60. # arcmin to deg
    plate_scale_arcsec = plate_scale * 3600.0

    if not instrument_spec["dither"]:
        dither_on = False
    else:
        dither_on = not no_dither
    if dither_params is None:
        dither_params = [8.0, 8.0, 1000.0, 707.0]
    dither_dict = {"x_amp": dither_params[0],
                   "y_amp": dither_params[1],
                   "x_period": dither_params[2],
                   "y_period": dither_params[3],
                   "dither_on": dither_on,
                   "plate_scale": plate_scale_arcsec}

    event_params = {}
    event_params["exposure_time"] = exp_time
    event_params["arf"] = arf.filename
    event_params["sky_center"] = sky_center
    event_params["pix_center"] = np.array([0.5*(2*nx+1)]*2)
    event_params["num_pixels"] = nx
    event_params["plate_scale"] = plate_scale
    event_params["rmf"] = rmf.filename
    event_params["channel_type"] = rmf.header["CHANTYPE"]
    event_params["telescope"] = rmf.header["TELESCOP"]
    event_params["instrument"] = instrument_spec['name']
    event_params["mission"] = rmf.header.get("MISSION", "")
    event_params["nchan"] = rmf.n_ch
    event_params["roll_angle"] = roll_angle
    event_params["fov"] = instrument_spec["fov"]
    event_params["chan_lim"] = [rmf.cmin, rmf.cmax]
    event_params["chips"] = instrument_spec["chips"]
    event_params["dither_params"] = dither_dict
    event_params["aimpt_coords"] = instrument_spec["aimpt_coords"]

    w = pywcs.WCS(naxis=2)
    w.wcs.crval = event_params["sky_center"]
    w.wcs.crpix = event_params["pix_center"]
    w.wcs.cdelt = [-plate_scale, plate_scale]
    w.wcs.ctype = ["RA---TAN","DEC--TAN"]
    w.wcs.cunit = ["deg"]*2

    rot_mat = get_rot_mat(roll_angle)

    all_events = defaultdict(list)

    for i, evts in enumerate(event_list):

        mylog.info("Detecting events from source %s." % parameters["sources"][i])

        # Step 1: Use ARF to determine which photons are observed

        mylog.info("Applying energy-dependent effective area from %s." % os.path.split(arf.filename)[-1])
        refband = [parameters["emin"][i], parameters["emax"][i]]
        events = arf.detect_events(evts, exp_time, parameters["flux"][i], refband, prng=prng)

        n_evt = events["energy"].size

        if n_evt == 0:
            mylog.warning("No events were observed for this source!!!")
        else:

            # Step 2: Assign pixel coordinates to events. Apply dithering and
            # PSF. Clip events that don't fall within the detection region.

            mylog.info("Pixeling events.")

            # Convert RA, Dec to pixel coordinates
            xpix, ypix = w.wcs_world2pix(events["ra"], events["dec"], 1)

            xpix -= event_params["pix_center"][0]
            ypix -= event_params["pix_center"][1]

            events.pop("ra")
            events.pop("dec")

            n_evt = xpix.size

            # Rotate physical coordinates to detector coordinates

            det = np.dot(rot_mat, np.array([xpix, ypix]))
            detx = det[0,:] + event_params["aimpt_coords"][0]
            dety = det[1,:] + event_params["aimpt_coords"][1]

            # Add times to events
            events['time'] = prng.uniform(size=n_evt, low=0.0,
                                          high=event_params["exposure_time"])

            # Apply dithering

            x_offset, y_offset = perform_dither(events["time"], dither_dict)

            detx -= x_offset
            dety -= y_offset

            # PSF scattering of detector coordinates

            if instrument_spec["psf"] is not None:
                psf_type, psf_spec = instrument_spec["psf"]
                if psf_type == "gaussian":
                    sigma = psf_spec/sigma_to_fwhm/plate_scale_arcsec
                    detx += prng.normal(loc=0.0, scale=sigma, size=n_evt)
                    dety += prng.normal(loc=0.0, scale=sigma, size=n_evt)
                else:
                    raise NotImplementedError("PSF type %s not implemented!" % psf_type)

            # Convert detector coordinates to chip coordinates.
            # Throw out events that don't fall on any chip.

            cx = np.trunc(detx)+0.5*np.sign(detx)
            cy = np.trunc(dety)+0.5*np.sign(dety)

            if event_params["chips"] is None:
                events["chip_id"] = np.zeros(n_evt, dtype='int')
                keepx = np.logical_and(cx >= -0.5*nx, cx <= 0.5*nx)
                keepy = np.logical_and(cy >= -0.5*nx, cy <= 0.5*nx)
                keep = np.logical_and(keepx, keepy)
            else:
                events["chip_id"] = -np.ones(n_evt, dtype='int')
                for i, chip in enumerate(event_params["chips"]):
                    thisc = np.ones(n_evt, dtype='bool')
                    rtype = chip[0]
                    args = chip[1:]
                    r = getattr(rfilter, rtype)(*args)
                    inside = r.inside(cx, cy)
                    thisc = np.logical_and(thisc, inside)
                    events["chip_id"][thisc] = i
                keep = events["chip_id"] > -1

            mylog.info("%d events were rejected because " % (n_evt-keep.sum()) +
                       "they do not fall on any CCD.")
            n_evt = keep.sum()

            if n_evt == 0:
                mylog.warning("No events are within the field of view for this source!!!")
            else:

                # Keep only those events which fall on a chip

                for key in events:
                    events[key] = events[key][keep]

                # Convert chip coordinates back to detector coordinates, unless the
                # user has specified that they want subpixel resolution

                if subpixel_res:
                    events["detx"] = detx[keep]
                    events["dety"] = dety[keep]
                else:
                    events["detx"] = cx[keep] + prng.uniform(low=-0.5, high=0.5, size=n_evt)
                    events["dety"] = cy[keep] + prng.uniform(low=-0.5, high=0.5, size=n_evt)

                # Convert detector coordinates back to pixel coordinates by
                # adding the dither offsets back in and applying the rotation
                # matrix again

                det = np.array([events["detx"] + x_offset[keep] - event_params["aimpt_coords"][0],
                                events["dety"] + y_offset[keep] - event_params["aimpt_coords"][1]])
                pix = np.dot(rot_mat.T, det)

                events["xpix"] = pix[0,:] + event_params['pix_center'][0]
                events["ypix"] = pix[1,:] + event_params['pix_center'][1]

        if n_evt > 0:
            for key in events:
                all_events[key] = np.concatenate([all_events[key], events[key]])

    if len(all_events["energy"]) == 0:
        mylog.warning("No events from any of the sources in the catalog were detected!")
        for key in ["xpix", "ypix", "detx", "dety", "time", "chip_id", event_params["channel_type"]]:
            all_events[key] = np.array([])
    else:
        # Step 4: Scatter energies with RMF
        mylog.info("Scattering energies with RMF %s." % os.path.split(rmf.filename)[-1])
        all_events = rmf.scatter_energies(all_events, prng=prng)

    return all_events, event_params
Exemplo n.º 12
0
 def rmf(self):
     if self._rmf is None:
         self._rmf = RedistributionMatrixFile(self.rmf_file)
     return self._rmf
Exemplo n.º 13
0
def plot_spectrum(specfile,
                  plot_energy=True,
                  lw=2,
                  xmin=None,
                  xmax=None,
                  ymin=None,
                  ymax=None,
                  xscale=None,
                  yscale=None,
                  label=None,
                  fontsize=18,
                  fig=None,
                  ax=None,
                  plot_counts=False,
                  **kwargs):
    """
    Make a quick Matplotlib plot of a convolved spectrum
    from a file. A Matplotlib figure and axis is returned.

    Parameters
    ----------
    specfile : string
        The file to be opened for plotting.
    figsize : tuple of integers, optional
        The size of the figure on both sides in inches.
        Default: (10,10)
    plot_energy : boolean, optional
        Whether to plot in energy or channel space. Default is
        to plot in energy, unless the RMF for the spectrum
        cannot be found. 
    lw : float, optional
        The width of the lines in the plots. Default: 2.0 px.
    xmin : float, optional
        The left-most energy (in keV) or channel to plot. Default is the 
        minimum value in the spectrum. 
    xmax : float, optional
        The right-most energy (in keV) or channel to plot. Default is the 
        maximum value in the spectrum. 
    ymin : float, optional
        The lower extent of the y-axis. By default it is set automatically.
    ymax : float, optional
        The upper extent of the y-axis. By default it is set automatically.
    xscale : string, optional
        The scaling of the x-axis of the plot. Default: "log"
    yscale : string, optional
        The scaling of the y-axis of the plot. Default: "log"
    label : string, optional
        The label of the spectrum. Default: None
    fontsize : int
        Font size for labels and axes. Default: 18
    fig : :class:`~matplotlib.figure.Figure`, optional
        A Figure instance to plot in. Default: None, one will be
        created if not provided.
    ax : :class:`~matplotlib.axes.Axes`, optional
        An Axes instance to plot in. Default: None, one will be
        created if not provided.
    plot_counts : boolean, optional
        If set to True, the counts instead of the count rate will
        be plotted. Default: False

    Returns
    -------
    A tuple of the :class:`~matplotlib.figure.Figure` and the :class:`~matplotlib.axes.Axes` objects.
    """
    import matplotlib.pyplot as plt
    from soxs.instrument import RedistributionMatrixFile
    f = pyfits.open(specfile)
    hdu = f["SPECTRUM"]
    chantype = hdu.header["CHANTYPE"]
    rmf = hdu.header.get("RESPFILE", None)
    xerr = None
    if plot_energy:
        if rmf is not None:
            rmf = RedistributionMatrixFile(rmf)
            x = 0.5 * (rmf.ebounds_data["E_MIN"] + rmf.ebounds_data["E_MAX"])
            xerr = 0.5 * (rmf.ebounds_data["E_MAX"] -
                          rmf.ebounds_data["E_MIN"])
            xlabel = "Energy (keV)"
        else:
            raise RuntimeError("Cannot find the RMF associated with this "
                               "spectrum, so I cannot plot in energy!")
    else:
        x = hdu.data[chantype]
        xlabel = "Channel (%s)" % chantype
    if plot_counts:
        y = hdu.data["COUNTS"].astype("float64")
        yerr = np.sqrt(y)
    else:
        if "COUNT_RATE" in hdu.columns.names:
            y = hdu.data["COUNT_RATE"]
        else:
            y = hdu.data["COUNTS"] / hdu.header["EXPOSURE"]
        yerr = np.sqrt(hdu.data["COUNTS"]) / hdu.header["EXPOSURE"]
    if plot_energy:
        yunit = "keV"
        y /= 2.0 * xerr
        yerr /= 2.0 * xerr
    else:
        yunit = "bin"
    f.close()
    if fig is None:
        fig = plt.figure(figsize=(10, 10))
    if xscale is None:
        if ax is None:
            xscale = "log"
        else:
            xscale = ax.get_xscale()
    if yscale is None:
        if ax is None:
            yscale = "log"
        else:
            yscale = ax.get_yscale()
    if ax is None:
        ax = fig.add_subplot(111)
    ax.errorbar(x, y, yerr=yerr, xerr=xerr, lw=lw, label=label, **kwargs)
    ax.set_xscale(xscale)
    ax.set_yscale(yscale)
    ax.set_xlim(xmin, xmax)
    ax.set_ylim(ymin, ymax)
    ax.set_xlabel(xlabel, fontsize=fontsize)
    if plot_counts:
        ylabel = "Counts (counts/%s)"
    else:
        ylabel = "Count Rate (counts/s/%s)"
    ax.set_ylabel(ylabel % yunit, fontsize=fontsize)
    ax.tick_params(axis='both', labelsize=fontsize)
    return fig, ax
Exemplo n.º 14
0
def generate_events(input_events, exp_time, instrument, sky_center, 
                    no_dither=False, dither_params=None, 
                    roll_angle=0.0, subpixel_res=False, prng=None):
    """
    Take unconvolved events and convolve them with instrumental responses. This 
    function does the following:

    1. Determines which events are observed using the ARF
    2. Pixelizes the events, applying PSF effects and dithering
    3. Determines energy channels using the RMF

    This function is not meant to be called by the end-user but is used by
    the :func:`~soxs.instrument.instrument_simulator` function.

    Parameters
    ----------
    input_events : string, dict, or None
        The unconvolved events to be used as input. Can be one of the
        following:
        1. The name of a SIMPUT catalog file.
        2. A Python dictionary containing the following items:
        "ra": A NumPy array of right ascension values in degrees.
        "dec": A NumPy array of declination values in degrees.
        "energy": A NumPy array of energy values in keV.
        "flux": The flux of the entire source, in units of erg/cm**2/s.
    out_file : string
        The name of the event file to be written.
    exp_time : float, (value, unit) tuple, or :class:`~astropy.units.Quantity`
        The exposure time to use, in seconds. 
    instrument : string
        The name of the instrument to use, which picks an instrument
        specification from the instrument registry. 
    sky_center : array, tuple, or list
        The center RA, Dec coordinates of the observation, in degrees.
    no_dither : boolean, optional
        If True, turn off dithering entirely. Default: False
    dither_params : array-like of floats, optional
        The parameters to use to control the size and period of the dither
        pattern. The first two numbers are the dither amplitude in x and y
        detector coordinates in arcseconds, and the second two numbers are
        the dither period in x and y detector coordinates in seconds. 
        Default: [8.0, 8.0, 1000.0, 707.0].
    roll_angle : float, (value, unit) tuple, or :class:`~astropy.units.Quantity`, optional
        The roll angle of the observation in degrees. Default: 0.0
    subpixel_res: boolean, optional
        If True, event positions are not randomized within the pixels 
        within which they are detected. Default: False
    prng : :class:`~numpy.random.RandomState` object, integer, or None
        A pseudo-random number generator. Typically will only 
        be specified if you have a reason to generate the same 
        set of random numbers, such as for a test. Default is None, 
        which sets the seed based on the system time. 
    """
    import pyregion._region_filter as rfilter
    exp_time = parse_value(exp_time, "s")
    roll_angle = parse_value(roll_angle, "deg")
    prng = parse_prng(prng)
    if isinstance(input_events, dict):
        parameters = {}
        for key in ["flux", "emin", "emax", "sources"]:
            parameters[key] = input_events[key]
        event_list = []
        for i in range(len(parameters["flux"])):
            edict = {}
            for key in ["ra", "dec", "energy"]:
                edict[key] = input_events[key][i]
            event_list.append(edict)
    elif isinstance(input_events, string_types):
        # Assume this is a SIMPUT catalog
        event_list, parameters = read_simput_catalog(input_events)

    try:
        instrument_spec = instrument_registry[instrument]
    except KeyError:
        raise KeyError("Instrument %s is not in the instrument registry!" % instrument)
    if not instrument_spec["imaging"]:
        raise RuntimeError("Instrument '%s' is not " % instrument_spec["name"] +
                           "designed for imaging observations!")

    arf_file = get_response_path(instrument_spec["arf"])
    rmf_file = get_response_path(instrument_spec["rmf"])
    arf = AuxiliaryResponseFile(arf_file)
    rmf = RedistributionMatrixFile(rmf_file)

    nx = instrument_spec["num_pixels"]
    plate_scale = instrument_spec["fov"]/nx/60. # arcmin to deg
    plate_scale_arcsec = plate_scale * 3600.0

    if not instrument_spec["dither"]:
        dither_on = False
    else:
        dither_on = not no_dither
    if dither_params is None:
        dither_params = [8.0, 8.0, 1000.0, 707.0]
    dither_dict = {"x_amp": dither_params[0],
                   "y_amp": dither_params[1],
                   "x_period": dither_params[2],
                   "y_period": dither_params[3],
                   "dither_on": dither_on,
                   "plate_scale": plate_scale_arcsec}

    event_params = {}
    event_params["exposure_time"] = exp_time
    event_params["arf"] = arf.filename
    event_params["sky_center"] = sky_center
    event_params["pix_center"] = np.array([0.5*(2*nx+1)]*2)
    event_params["num_pixels"] = nx
    event_params["plate_scale"] = plate_scale
    event_params["rmf"] = rmf.filename
    event_params["channel_type"] = rmf.header["CHANTYPE"]
    event_params["telescope"] = rmf.header["TELESCOP"]
    event_params["instrument"] = instrument_spec['name']
    event_params["mission"] = rmf.header.get("MISSION", "")
    event_params["nchan"] = rmf.n_ch
    event_params["roll_angle"] = roll_angle
    event_params["fov"] = instrument_spec["fov"]
    event_params["chan_lim"] = [rmf.cmin, rmf.cmax]
    event_params["chips"] = instrument_spec["chips"]
    event_params["dither_params"] = dither_dict
    event_params["aimpt_coords"] = instrument_spec["aimpt_coords"]

    w = pywcs.WCS(naxis=2)
    w.wcs.crval = event_params["sky_center"]
    w.wcs.crpix = event_params["pix_center"]
    w.wcs.cdelt = [-plate_scale, plate_scale]
    w.wcs.ctype = ["RA---TAN","DEC--TAN"]
    w.wcs.cunit = ["deg"]*2

    rot_mat = get_rot_mat(roll_angle)

    all_events = defaultdict(list)

    for i, evts in enumerate(event_list):

        mylog.info("Detecting events from source %s." % parameters["sources"][i])

        # Step 1: Use ARF to determine which photons are observed

        mylog.info("Applying energy-dependent effective area from %s." % os.path.split(arf.filename)[-1])
        refband = [parameters["emin"][i], parameters["emax"][i]]
        events = arf.detect_events(evts, exp_time, parameters["flux"][i], refband, prng=prng)

        n_evt = events["energy"].size

        if n_evt == 0:
            mylog.warning("No events were observed for this source!!!")
        else:

            # Step 2: Assign pixel coordinates to events. Apply dithering and
            # PSF. Clip events that don't fall within the detection region.

            mylog.info("Pixeling events.")

            # Convert RA, Dec to pixel coordinates
            xpix, ypix = w.wcs_world2pix(events["ra"], events["dec"], 1)

            xpix -= event_params["pix_center"][0]
            ypix -= event_params["pix_center"][1]

            events.pop("ra")
            events.pop("dec")

            n_evt = xpix.size

            # Rotate physical coordinates to detector coordinates

            det = np.dot(rot_mat, np.array([xpix, ypix]))
            detx = det[0,:] + event_params["aimpt_coords"][0]
            dety = det[1,:] + event_params["aimpt_coords"][1]

            # Add times to events
            events['time'] = prng.uniform(size=n_evt, low=0.0,
                                          high=event_params["exposure_time"])

            # Apply dithering

            x_offset, y_offset = perform_dither(events["time"], dither_dict)

            detx -= x_offset
            dety -= y_offset

            # PSF scattering of detector coordinates

            if instrument_spec["psf"] is not None:
                psf_type, psf_spec = instrument_spec["psf"]
                if psf_type == "gaussian":
                    sigma = psf_spec/sigma_to_fwhm/plate_scale_arcsec
                    detx += prng.normal(loc=0.0, scale=sigma, size=n_evt)
                    dety += prng.normal(loc=0.0, scale=sigma, size=n_evt)
                else:
                    raise NotImplementedError("PSF type %s not implemented!" % psf_type)

            # Convert detector coordinates to chip coordinates.
            # Throw out events that don't fall on any chip.

            cx = np.trunc(detx)+0.5*np.sign(detx)
            cy = np.trunc(dety)+0.5*np.sign(dety)

            if event_params["chips"] is None:
                events["chip_id"] = np.zeros(n_evt, dtype='int')
                keepx = np.logical_and(cx >= -0.5*nx, cx <= 0.5*nx)
                keepy = np.logical_and(cy >= -0.5*nx, cy <= 0.5*nx)
                keep = np.logical_and(keepx, keepy)
            else:
                events["chip_id"] = -np.ones(n_evt, dtype='int')
                for i, chip in enumerate(event_params["chips"]):
                    thisc = np.ones(n_evt, dtype='bool')
                    rtype = chip[0]
                    args = chip[1:]
                    r = getattr(rfilter, rtype)(*args)
                    inside = r.inside(cx, cy)
                    thisc = np.logical_and(thisc, inside)
                    events["chip_id"][thisc] = i
                keep = events["chip_id"] > -1

            mylog.info("%d events were rejected because " % (n_evt-keep.sum()) +
                       "they do not fall on any CCD.")
            n_evt = keep.sum()

            if n_evt == 0:
                mylog.warning("No events are within the field of view for this source!!!")
            else:

                # Keep only those events which fall on a chip

                for key in events:
                    events[key] = events[key][keep]

                # Convert chip coordinates back to detector coordinates, unless the
                # user has specified that they want subpixel resolution

                if subpixel_res:
                    events["detx"] = detx[keep]
                    events["dety"] = dety[keep]
                else:
                    events["detx"] = cx[keep] + prng.uniform(low=-0.5, high=0.5, size=n_evt)
                    events["dety"] = cy[keep] + prng.uniform(low=-0.5, high=0.5, size=n_evt)

                # Convert detector coordinates back to pixel coordinates by
                # adding the dither offsets back in and applying the rotation
                # matrix again

                det = np.array([events["detx"] + x_offset[keep] - event_params["aimpt_coords"][0],
                                events["dety"] + y_offset[keep] - event_params["aimpt_coords"][1]])
                pix = np.dot(rot_mat.T, det)

                events["xpix"] = pix[0,:] + event_params['pix_center'][0]
                events["ypix"] = pix[1,:] + event_params['pix_center'][1]

        if n_evt > 0:
            for key in events:
                all_events[key] = np.concatenate([all_events[key], events[key]])

    if len(all_events["energy"]) == 0:
        mylog.warning("No events from any of the sources in the catalog were detected!")
        for key in ["xpix", "ypix", "detx", "dety", "time", "chip_id", event_params["channel_type"]]:
            all_events[key] = np.array([])
    else:
        # Step 4: Scatter energies with RMF
        mylog.info("Scattering energies with RMF %s." % os.path.split(rmf.filename)[-1])
        all_events = rmf.scatter_energies(all_events, prng=prng)

    return all_events, event_params
Exemplo n.º 15
0
def simulate_spectrum(spec, instrument, exp_time, out_file,
                      instr_bkgnd=False, foreground=False,
                      ptsrc_bkgnd=False, bkgnd_area=None,
                      absorb_model="wabs", nH=0.05,
                      overwrite=False, prng=None):
    """
    Generate a PI or PHA spectrum from a :class:`~soxs.spectra.Spectrum`
    by convolving it with responses. To be used if one wants to 
    create a spectrum without worrying about spatial response. Similar
    to XSPEC's "fakeit".

    Parameters
    ----------
    spec : :class:`~soxs.spectra.Spectrum`
        The spectrum to be convolved. If None is supplied, only backgrounds
        will be simulated (if they are turned on).
    instrument : string
        The name of the instrument to use, which picks an instrument
        specification from the instrument registry.
    exp_time : float, (value, unit) tuple, or :class:`~astropy.units.Quantity`
        The exposure time in seconds.
    out_file : string
        The file to write the spectrum to.
    instr_bkgnd : boolean, optional
        Whether or not to include the instrumental/particle background. 
        Default: False
    foreground : boolean, optional
        Whether or not to include the local foreground.
        Default: False
    ptsrc_bkgnd : boolean, optional
        Whether or not to include the unresolved point-source background. 
        Default: False
    bkgnd_area : float, (value, unit) tuple, or :class:`~astropy.units.Quantity`
        The area on the sky for the background components, in square arcminutes.
        Default: None, necessary to specify if any of the background components
        are turned on. 
    absorb_model : string, optional
        The absorption model to use, "wabs" or "tbabs". Default: "wabs"
    nH : float, optional
        The hydrogen column in units of 10**22 atoms/cm**2. 
        Default: 0.05
    overwrite : boolean, optional
        Whether or not to overwrite an existing file. Default: False
    prng : :class:`~numpy.random.RandomState` object, integer, or None
        A pseudo-random number generator. Typically will only 
        be specified if you have a reason to generate the same 
        set of random numbers, such as for a test. Default is None, 
        which sets the seed based on the system time. 

    Examples
    --------
    >>> spec = soxs.Spectrum.from_file("my_spectrum.txt")
    >>> soxs.simulate_spectrum(spec, "lynx_lxm", 100000.0, 
    ...                        "my_spec.pi", overwrite=True)
    """
    from soxs.events import _write_spectrum
    from soxs.instrument import RedistributionMatrixFile, \
        AuxiliaryResponseFile
    from soxs.spectra import ConvolvedSpectrum
    from soxs.background.foreground import hm_astro_bkgnd
    from soxs.background.instrument import instrument_backgrounds
    from soxs.background.spectra import BackgroundSpectrum
    prng = parse_prng(prng)
    exp_time = parse_value(exp_time, "s")
    try:
        instrument_spec = instrument_registry[instrument]
    except KeyError:
        raise KeyError("Instrument %s is not in the instrument registry!" % instrument)
    if foreground or instr_bkgnd or ptsrc_bkgnd:
        if instrument_spec["grating"]:
            raise NotImplementedError("Backgrounds cannot be included in simulations "
                                      "of gratings spectra at this time!")
        if bkgnd_area is None:
            raise RuntimeError("The 'bkgnd_area' argument must be set if one wants "
                               "to simulate backgrounds! Specify a value in square "
                               "arcminutes.")
        bkgnd_area = np.sqrt(parse_value(bkgnd_area, "arcmin**2"))
    elif spec is None:
        raise RuntimeError("You have specified no source spectrum and no backgrounds!")
    arf_file = get_response_path(instrument_spec["arf"])
    rmf_file = get_response_path(instrument_spec["rmf"])
    arf = AuxiliaryResponseFile(arf_file)
    rmf = RedistributionMatrixFile(rmf_file)

    event_params = {}
    event_params["RESPFILE"] = os.path.split(rmf.filename)[-1]
    event_params["ANCRFILE"] = os.path.split(arf.filename)[-1]
    event_params["TELESCOP"] = rmf.header["TELESCOP"]
    event_params["INSTRUME"] = rmf.header["INSTRUME"]
    event_params["MISSION"] = rmf.header.get("MISSION", "")

    out_spec = np.zeros(rmf.n_ch)

    if spec is not None:
        cspec = ConvolvedSpectrum(spec, arf)
        out_spec += rmf.convolve_spectrum(cspec, exp_time, prng=prng)

    fov = None if bkgnd_area is None else np.sqrt(bkgnd_area)

    if foreground:
        mylog.info("Adding in astrophysical foreground.")
        cspec_frgnd = ConvolvedSpectrum(hm_astro_bkgnd.to_spectrum(fov), arf)
        out_spec += rmf.convolve_spectrum(cspec_frgnd, exp_time, prng=prng)
    if instr_bkgnd and instrument_spec["bkgnd"] is not None:
        mylog.info("Adding in instrumental background.")
        instr_spec = instrument_backgrounds[instrument_spec["bkgnd"]]
        cspec_instr = instr_spec.to_scaled_spectrum(fov,
                                                    instrument_spec["focal_length"])
        out_spec += rmf.convolve_spectrum(cspec_instr, exp_time, prng=prng)
    if ptsrc_bkgnd:
        mylog.info("Adding in background from unresolved point-sources.")
        spec_plaw = BackgroundSpectrum.from_powerlaw(1.45, 0.0, 2.0e-7, emin=0.01,
                                                     emax=10.0, nbins=300000)
        spec_plaw.apply_foreground_absorption(nH, model=absorb_model)
        cspec_plaw = ConvolvedSpectrum(spec_plaw.to_spectrum(fov), arf)
        out_spec += rmf.convolve_spectrum(cspec_plaw, exp_time, prng=prng)

    bins = (np.arange(rmf.n_ch)+rmf.cmin).astype("int32")

    _write_spectrum(bins, out_spec, exp_time, rmf.header["CHANTYPE"], 
                    event_params, out_file, overwrite=overwrite)
Exemplo n.º 16
0
def simulate_spectrum(spec, instrument, exp_time, out_file,
                      instr_bkgnd=False, foreground=False,
                      ptsrc_bkgnd=False, bkgnd_area=None,
                      absorb_model="wabs", nH=0.05,
                      overwrite=False, prng=None):
    """
    Generate a PI or PHA spectrum from a :class:`~soxs.spectra.Spectrum`
    by convolving it with responses. To be used if one wants to 
    create a spectrum without worrying about spatial response. Similar
    to XSPEC's "fakeit".

    Parameters
    ----------
    spec : :class:`~soxs.spectra.Spectrum`
        The spectrum to be convolved. If None is supplied, only backgrounds
        will be simulated (if they are turned on).
    instrument : string
        The name of the instrument to use, which picks an instrument
        specification from the instrument registry.
    exp_time : float, (value, unit) tuple, or :class:`~astropy.units.Quantity`
        The exposure time in seconds.
    out_file : string
        The file to write the spectrum to.
    instr_bkgnd : boolean, optional
        Whether or not to include the instrumental/particle background. 
        Default: False
    foreground : boolean, optional
        Whether or not to include the local foreground.
        Default: False
    ptsrc_bkgnd : boolean, optional
        Whether or not to include the unresolved point-source background. 
        Default: False
    bkgnd_area : float, (value, unit) tuple, or :class:`~astropy.units.Quantity`
        The area on the sky for the background components, in square arcminutes.
        Default: None, necessary to specify if any of the background components
        are turned on. 
    absorb_model : string, optional
        The absorption model to use, "wabs" or "tbabs". Default: "wabs"
    nH : float, optional
        The hydrogen column in units of 10**22 atoms/cm**2. 
        Default: 0.05
    overwrite : boolean, optional
        Whether or not to overwrite an existing file. Default: False
    prng : :class:`~numpy.random.RandomState` object, integer, or None
        A pseudo-random number generator. Typically will only 
        be specified if you have a reason to generate the same 
        set of random numbers, such as for a test. Default is None, 
        which sets the seed based on the system time. 

    Examples
    --------
    >>> spec = soxs.Spectrum.from_file("my_spectrum.txt")
    >>> soxs.simulate_spectrum(spec, "lynx_lxm", 100000.0, 
    ...                        "my_spec.pi", overwrite=True)
    """
    from soxs.events import _write_spectrum
    from soxs.instrument import RedistributionMatrixFile, \
        AuxiliaryResponseFile
    from soxs.spectra import ConvolvedSpectrum
    from soxs.background.foreground import hm_astro_bkgnd
    from soxs.background.instrument import instrument_backgrounds
    from soxs.background.spectra import BackgroundSpectrum, \
        ConvolvedBackgroundSpectrum
    prng = parse_prng(prng)
    exp_time = parse_value(exp_time, "s")
    try:
        instrument_spec = instrument_registry[instrument]
    except KeyError:
        raise KeyError("Instrument %s is not in the instrument registry!" % instrument)
    if foreground or instr_bkgnd or ptsrc_bkgnd:
        if instrument_spec["grating"]:
            raise NotImplementedError("Backgrounds cannot be included in simulations "
                                      "of gratings spectra at this time!")
        if bkgnd_area is None:
            raise RuntimeError("The 'bkgnd_area' argument must be set if one wants "
                               "to simulate backgrounds! Specify a value in square "
                               "arcminutes.")
        bkgnd_area = np.sqrt(parse_value(bkgnd_area, "arcmin**2"))
    elif spec is None:
        raise RuntimeError("You have specified no source spectrum and no backgrounds!")
    arf_file = get_response_path(instrument_spec["arf"])
    rmf_file = get_response_path(instrument_spec["rmf"])
    arf = AuxiliaryResponseFile(arf_file)
    rmf = RedistributionMatrixFile(rmf_file)

    event_params = {}
    event_params["RESPFILE"] = os.path.split(rmf.filename)[-1]
    event_params["ANCRFILE"] = os.path.split(arf.filename)[-1]
    event_params["TELESCOP"] = rmf.header["TELESCOP"]
    event_params["INSTRUME"] = rmf.header["INSTRUME"]
    event_params["MISSION"] = rmf.header.get("MISSION", "")

    out_spec = np.zeros(rmf.n_ch)

    if spec is not None:
        cspec = ConvolvedSpectrum(spec, arf)
        out_spec += rmf.convolve_spectrum(cspec, exp_time, prng=prng)

    fov = None if bkgnd_area is None else np.sqrt(bkgnd_area)

    if foreground:
        mylog.info("Adding in astrophysical foreground.")
        cspec_frgnd = ConvolvedSpectrum(hm_astro_bkgnd.to_spectrum(fov), arf)
        out_spec += rmf.convolve_spectrum(cspec_frgnd, exp_time, prng=prng)
    if instr_bkgnd and instrument_spec["bkgnd"] is not None:
        mylog.info("Adding in instrumental background.")
        instr_spec = instrument_backgrounds[instrument_spec["bkgnd"]]
        cspec_instr = instr_spec.to_scaled_spectrum(fov,
                                                    instrument_spec["focal_length"])
        out_spec += rmf.convolve_spectrum(cspec_instr, exp_time, prng=prng)
    if ptsrc_bkgnd:
        mylog.info("Adding in background from unresolved point-sources.")
        spec_plaw = BackgroundSpectrum.from_powerlaw(1.45, 0.0, 2.0e-7, emin=0.01,
                                                     emax=10.0, nbins=300000)
        spec_plaw.apply_foreground_absorption(nH, model=absorb_model)
        cspec_plaw = ConvolvedBackgroundSpectrum(spec_plaw.to_spectrum(fov), arf)
        out_spec += rmf.convolve_spectrum(cspec_plaw, exp_time, prng=prng)

    bins = (np.arange(rmf.n_ch)+rmf.cmin).astype("int32")

    _write_spectrum(bins, out_spec, exp_time, rmf.header["CHANTYPE"], 
                    event_params, out_file, overwrite=overwrite)
Exemplo n.º 17
0
Arquivo: events.py Projeto: eblur/soxs
def write_spectrum(evtfile, specfile, overwrite=False):
    r"""
    Bin event energies into a spectrum and write it to 
    a FITS binary table. Does not do any grouping of 
    channels, and will automatically determine PI or PHA. 

    Parameters
    ----------
    evtfile : string
        The name of the event file to read the events from. 
    specfile : string
        The name of the spectrum file to be written.
    overwrite : boolean, optional
        Whether or not to overwrite an existing file with 
        the same name. Default: False
    """
    from soxs.instrument import RedistributionMatrixFile
    parameters = {}
    if isinstance(evtfile, string_types):
        f = pyfits.open(evtfile)
        spectype = f["EVENTS"].header["CHANTYPE"]
        rmf = f["EVENTS"].header["RESPFILE"]
        p = f["EVENTS"].data[spectype]
        exp_time = f["EVENTS"].header["EXPOSURE"]
        for key in ["RESPFILE", "ANCRFILE", "MISSION", "TELESCOP", "INSTRUME"]:
            parameters[key] = f["EVENTS"].header[key]
        f.close()
    else:
        rmf = evtfile["rmf"]
        spectype = evtfile["channel_type"]
        p = evtfile[spectype]
        parameters["RESPFILE"] = os.path.split(rmf)[-1]
        parameters["ANCRFILE"] = os.path.split(evtfile["arf"])[-1]
        parameters["TELESCOP"] = evtfile["telescope"]
        parameters["INSTRUME"] = evtfile["instrument"]
        parameters["MISSION"] = evtfile["mission"]
        exp_time = evtfile["exposure_time"]

    rmf = RedistributionMatrixFile(rmf)
    minlength = rmf.n_ch
    if rmf.cmin == 1:
        minlength += 1
    spec = np.bincount(p, minlength=minlength)
    if rmf.cmin == 1:
        spec = spec[1:]
    bins = (np.arange(rmf.n_ch) + rmf.cmin).astype("int32")

    col1 = pyfits.Column(name='CHANNEL', format='1J', array=bins)
    col2 = pyfits.Column(name=spectype.upper(),
                         format='1D',
                         array=bins.astype("float64"))
    col3 = pyfits.Column(name='COUNTS',
                         format='1J',
                         array=spec.astype("int32"))
    col4 = pyfits.Column(name='COUNT_RATE', format='1D', array=spec / exp_time)

    coldefs = pyfits.ColDefs([col1, col2, col3, col4])

    tbhdu = pyfits.BinTableHDU.from_columns(coldefs)
    tbhdu.name = "SPECTRUM"

    tbhdu.header["DETCHANS"] = spec.size
    tbhdu.header["TOTCTS"] = spec.sum()
    tbhdu.header["EXPOSURE"] = exp_time
    tbhdu.header["LIVETIME"] = exp_time
    tbhdu.header["CONTENT"] = spectype
    tbhdu.header["HDUCLASS"] = "OGIP"
    tbhdu.header["HDUCLAS1"] = "SPECTRUM"
    tbhdu.header["HDUCLAS2"] = "TOTAL"
    tbhdu.header["HDUCLAS3"] = "TYPE:I"
    tbhdu.header["HDUCLAS4"] = "COUNT"
    tbhdu.header["HDUVERS"] = "1.1.0"
    tbhdu.header["HDUVERS1"] = "1.1.0"
    tbhdu.header["CHANTYPE"] = spectype
    tbhdu.header["BACKFILE"] = "none"
    tbhdu.header["CORRFILE"] = "none"
    tbhdu.header["POISSERR"] = True
    for key in ["RESPFILE", "ANCRFILE", "MISSION", "TELESCOP", "INSTRUME"]:
        tbhdu.header[key] = parameters[key]
    tbhdu.header["AREASCAL"] = 1.0
    tbhdu.header["CORRSCAL"] = 0.0
    tbhdu.header["BACKSCAL"] = 1.0

    hdulist = pyfits.HDUList([pyfits.PrimaryHDU(), tbhdu])

    hdulist.writeto(specfile, overwrite=overwrite)
Exemplo n.º 18
0
    def write_channel_spectrum(self, specfile, overwrite=False):
        r"""
        Bin event channels into a spectrum and write it to a FITS binary table. 

        Parameters
        ----------
        specfile : string
            The name of the FITS file to be written.
        overwrite : boolean, optional
            Set to True to overwrite a previous file.
        """
        spectype = self.parameters["channel_type"]
        rmf = RedistributionMatrixFile(self.parameters["rmf"])
        minlength = rmf.n_ch
        if rmf.cmin == 1:
            minlength += 1
        spec = np.bincount(self[spectype], minlength=minlength)
        if rmf.cmin == 1:
            spec = spec[1:]
        bins = (np.arange(rmf.n_ch) + rmf.cmin).astype("int32")

        if parallel_capable:
            spec = comm.comm.reduce(spec, root=0)

        if comm.rank == 0:

            col1 = pyfits.Column(name='CHANNEL', format='1J', array=bins)
            col2 = pyfits.Column(name=spectype.upper(),
                                 format='1D',
                                 array=bins.astype("float64"))
            col3 = pyfits.Column(name='COUNTS',
                                 format='1J',
                                 array=spec.astype("int32"))
            col4 = pyfits.Column(name='COUNT_RATE',
                                 format='1D',
                                 array=spec /
                                 float(self.parameters["exp_time"]))

            coldefs = pyfits.ColDefs([col1, col2, col3, col4])

            tbhdu = pyfits.BinTableHDU.from_columns(coldefs)
            tbhdu.name = "SPECTRUM"

            tbhdu.header["DETCHANS"] = spec.shape[0]
            tbhdu.header["TOTCTS"] = spec.sum()
            tbhdu.header["EXPOSURE"] = float(self.parameters["exp_time"])
            tbhdu.header["LIVETIME"] = float(self.parameters["exp_time"])
            tbhdu.header["CONTENT"] = spectype
            tbhdu.header["HDUCLASS"] = "OGIP"
            tbhdu.header["HDUCLAS1"] = "SPECTRUM"
            tbhdu.header["HDUCLAS2"] = "TOTAL"
            tbhdu.header["HDUCLAS3"] = "TYPE:I"
            tbhdu.header["HDUCLAS4"] = "COUNT"
            tbhdu.header["HDUVERS"] = "1.1.0"
            tbhdu.header["HDUVERS1"] = "1.1.0"
            tbhdu.header["CHANTYPE"] = spectype
            tbhdu.header["BACKFILE"] = "none"
            tbhdu.header["CORRFILE"] = "none"
            tbhdu.header["POISSERR"] = True
            tbhdu.header["RESPFILE"] = os.path.split(
                self.parameters["rmf"])[-1]
            tbhdu.header["ANCRFILE"] = os.path.split(
                self.parameters["arf"])[-1]
            tbhdu.header["MISSION"] = self.parameters["mission"]
            tbhdu.header["TELESCOP"] = self.parameters["telescope"]
            tbhdu.header["INSTRUME"] = self.parameters["instrument"]
            tbhdu.header["AREASCAL"] = 1.0
            tbhdu.header["CORRSCAL"] = 0.0
            tbhdu.header["BACKSCAL"] = 1.0

            hdulist = pyfits.HDUList([pyfits.PrimaryHDU(), tbhdu])

            hdulist.writeto(specfile, overwrite=overwrite)

        comm.barrier()
Exemplo n.º 19
0
Arquivo: events.py Projeto: eblur/soxs
def plot_spectrum(specfile, plot_energy=True, lw=2, fig=None, ax=None):
    """
    Make a quick Matplotlib plot of a convolved spectrum
    from a file. A Matplotlib figure is returned.

    Parameters
    ----------
    specfile : string
        The file to be opened for plotting.
    figsize : tuple of integers, optional
        The size of the figure on both sides in inches.
        Default: (10,10)
    plot_energy : boolean, optional
        Whether to plot in energy or channel space. Default is
        to plot in energy, unless the RMF for the spectrum
        cannot be found. 
    lw : float, optional
        The width of the lines in the plots. Default: 2.0 px.
    fig : :class:`~matplotlib.figure.Figure`, optional
        A Figure instance to plot in. Default: None, one will be
        created if not provided.
    ax : :class:`~matplotlib.axes.Axes`, optional
        An Axes instance to plot in. Default: None, one will be
        created if not provided.

    Returns
    -------
    The :class:`~matplotlib.figure.Figure` object.
    """
    import matplotlib.pyplot as plt
    from soxs.instrument import RedistributionMatrixFile
    f = pyfits.open(specfile)
    hdu = f["SPECTRUM"]
    chantype = hdu.header["CHANTYPE"]
    rmf = hdu.header.get("RESPFILE", None)
    xerr = None
    if plot_energy:
        if rmf is not None:
            rmf = RedistributionMatrixFile(rmf)
            x = 0.5 * (rmf.ebounds["E_MIN"] + rmf.ebounds["E_MAX"])
            xerr = 0.5 * (rmf.ebounds["E_MAX"] - rmf.ebounds["E_MIN"])
            xlabel = "E (keV)"
        else:
            raise RuntimeError("Cannot find the RMF associated with this "
                               "spectrum, so I cannot plot in energy!")
    else:
        x = hdu.data[chantype]
        xlabel = "Channel (%s)" % chantype
    y = hdu.data["COUNT_RATE"]
    yerr = np.sqrt(hdu.data["COUNTS"]) / hdu.header["EXPOSURE"]
    if plot_energy:
        yunit = "keV"
        y /= 2.0 * xerr
        yerr /= 2.0 * xerr
    else:
        yunit = "bin"
    f.close()
    if fig is None:
        fig = plt.figure(figsize=(10, 10))
    if ax is None:
        ax = fig.add_subplot(111)
    ax.errorbar(x, y, yerr=yerr, xerr=xerr, lw=lw)
    ax.set_xlabel(xlabel)
    ax.set_ylabel("Count Rate (counts/s/%s)" % yunit)
    return fig
Exemplo n.º 20
0
from soxs.spectra import ApecGenerator
from soxs.spatial import PointSourceModel
from soxs.simput import SimputCatalog
from soxs.instrument_registry import \
    get_instrument_from_registry
from soxs.instrument import instrument_simulator, \
    RedistributionMatrixFile, simulate_spectrum
from soxs.events import write_spectrum
from numpy.random import RandomState
from numpy.testing import assert_allclose
from soxs.tests.utils import spectrum_answer_testing, \
    file_answer_testing

inst_name = "lynx_lxm"

rmf = RedistributionMatrixFile.from_instrument(inst_name)
agen0 = ApecGenerator(0.01, 10.0, 20000, broadening=True)
agen_var0 = ApecGenerator(0.01,
                          10.0,
                          20000,
                          var_elem=["O", "Fe"],
                          broadening=True)
agen_nolines0 = ApecGenerator(0.01, 10.0, 20000, broadening=True, nolines=True)
agen_aspl0 = ApecGenerator(0.01,
                           10.0,
                           20000,
                           broadening=True,
                           abund_table="aspl")
agen = ApecGenerator(rmf.elo[0], rmf.ehi[-1], rmf.n_e, broadening=True)
agen_var = ApecGenerator(rmf.elo[0],
                         rmf.ehi[-1],
Exemplo n.º 21
0
def simulate_spectrum(spec,
                      instrument,
                      exp_time,
                      out_file,
                      overwrite=False,
                      prng=None):
    """
    Generate a PI or PHA spectrum from a :class:`~soxs.spectra.Spectrum`
    by convolving it with responses. To be used if one wants to 
    create a spectrum without worrying about spatial response. Similar
    to XSPEC's "fakeit". 

    Parameters
    ----------
    spec : :class:`~soxs.spectra.Spectrum`
        The spectrum to be convolved.
    instrument : string
        The name of the instrument to use, which picks an instrument
        specification from the instrument registry. 
    exp_time : float, (value, unit) tuple, or :class:`~astropy.units.Quantity`
        The exposure time in seconds.
    out_file : string
        The file to write the spectrum to.
    overwrite : boolean, optional
        Whether or not to overwrite an existing file. Default: False
    prng : :class:`~numpy.random.RandomState` object, integer, or None
        A pseudo-random number generator. Typically will only 
        be specified if you have a reason to generate the same 
        set of random numbers, such as for a test. Default is None, 
        which sets the seed based on the system time. 

    Examples
    --------
    >>> spec = soxs.Spectrum.from_file("my_spectrum.txt")
    >>> soxs.simulate_spectrum(spec, "mucal", 100000.0, 
    ...                        "my_spec.pi", overwrite=True)
    """
    from soxs.events import write_spectrum
    from soxs.instrument import RedistributionMatrixFile, \
        AuxiliaryResponseFile
    from soxs.spectra import ConvolvedSpectrum
    prng = parse_prng(prng)
    exp_time = parse_value(exp_time, "s")
    try:
        instrument_spec = instrument_registry[instrument]
    except KeyError:
        raise KeyError("Instrument %s is not in the instrument registry!" %
                       instrument)
    arf_file = check_file_location(instrument_spec["arf"], "files")
    rmf_file = check_file_location(instrument_spec["rmf"], "files")
    arf = AuxiliaryResponseFile(arf_file)
    rmf = RedistributionMatrixFile(rmf_file)
    cspec = ConvolvedSpectrum(spec, arf)
    events = {}
    events["energy"] = cspec.generate_energies(exp_time, prng=prng).value
    events = rmf.scatter_energies(events, prng=prng)
    events["arf"] = arf.filename
    events["rmf"] = rmf.filename
    events["exposure_time"] = exp_time
    events["channel_type"] = rmf.header["CHANTYPE"]
    events["telescope"] = rmf.header["TELESCOP"]
    events["instrument"] = rmf.header["INSTRUME"]
    events["mission"] = rmf.header.get("MISSION", "")
    write_spectrum(events, out_file, overwrite=overwrite)