def make_diffuse_background(bkg_events, event_params, rmf, prng=None): from soxs.instrument import perform_dither n_e = bkg_events["energy"].size bkg_events['time'] = prng.uniform(size=n_e, low=0.0, high=event_params["exposure_time"]) x_offset, y_offset = perform_dither(bkg_events["time"], event_params["dither_params"]) rot_mat = get_rot_mat(event_params["roll_angle"]) det = np.array([ bkg_events["detx"] + x_offset - event_params["aimpt_coords"][0], bkg_events["dety"] + y_offset - event_params["aimpt_coords"][1] ]) pix = np.dot(rot_mat.T, det) bkg_events["xpix"] = pix[0, :] + event_params['pix_center'][0] bkg_events["ypix"] = pix[1, :] + event_params['pix_center'][1] mylog.info("Scattering energies with RMF %s." % os.path.split(rmf.filename)[-1]) bkg_events = rmf.scatter_energies(bkg_events, prng=prng) return bkg_events
def make_diffuse_background(bkg_events, event_params, rmf, prng=None): from soxs.instrument import perform_dither n_e = bkg_events["energy"].size bkg_events['time'] = prng.uniform(size=n_e, low=0.0, high=event_params["exposure_time"]) x_offset, y_offset = perform_dither(bkg_events["time"], event_params["dither_params"]) rot_mat = get_rot_mat(event_params["roll_angle"]) det = np.array([ bkg_events["detx"] + x_offset - event_params["aimpt_coords"][0] - event_params["aimpt_shift"][0], bkg_events["dety"] + y_offset - event_params["aimpt_coords"][1] - event_params["aimpt_shift"][1] ]) pix = np.dot(rot_mat.T, det) bkg_events["xpix"] = pix[0, :] + event_params['pix_center'][0] bkg_events["ypix"] = pix[1, :] + event_params['pix_center'][1] return bkg_events
def make_diffuse_background(bkg_events, event_params, rmf, prng=None): from soxs.instrument import perform_dither n_e = bkg_events["energy"].size bkg_events['time'] = prng.uniform(size=n_e, low=0.0, high=event_params["exposure_time"]) x_offset, y_offset = perform_dither(bkg_events["time"], event_params["dither_params"]) rot_mat = get_rot_mat(event_params["roll_angle"]) det = np.array([bkg_events["detx"] + x_offset - event_params["aimpt_coords"][0], bkg_events["dety"] + y_offset - event_params["aimpt_coords"][1]]) pix = np.dot(rot_mat.T, det) bkg_events["xpix"] = pix[0, :] + event_params['pix_center'][0] bkg_events["ypix"] = pix[1, :] + event_params['pix_center'][1] mylog.info("Scattering energies with RMF %s." % os.path.split(rmf.filename)[-1]) bkg_events = rmf.scatter_energies(bkg_events, prng=prng) return bkg_events
def add_background_from_file(events, event_params, bkg_file): from soxs.instrument import perform_dither f = pyfits.open(bkg_file) hdu = f["EVENTS"] dither_params = {} if "DITHXAMP" in hdu.header: dither_params["x_amp"] = hdu.header["DITHXAMP"] dither_params["y_amp"] = hdu.header["DITHYAMP"] dither_params["x_period"] = hdu.header["DITHXPER"] dither_params["y_period"] = hdu.header["DITHYPER"] dither_params["plate_scale"] = hdu.header["TCDLT3"] * 3600.0 dither_params["dither_on"] = True else: dither_params["dither_on"] = False sexp = event_params["exposure_time"] bexp = hdu.header["EXPOSURE"] if event_params["exposure_time"] > hdu.header["EXPOSURE"]: raise RuntimeError( "The background file does not have sufficient exposure! Source " "exposure time %g, background exposure time %g." % (sexp, bexp)) for k1, k2 in key_map.items(): if event_params[k1] != hdu.header[k2]: raise RuntimeError("'%s' keyword does not match! %s vs. %s" % (k1, event_params[k1], hdu.header[k2])) rmf1 = os.path.split(event_params["rmf"])[-1] rmf2 = hdu.header["RESPFILE"] arf1 = os.path.split(event_params["arf"])[-1] arf2 = hdu.header["ANCRFILE"] if rmf1 != rmf2: raise RuntimeError("RMFs do not match! %s vs. %s" % (rmf1, rmf2)) if arf1 != arf2: raise RuntimeError("ARFs do not match! %s vs. %s" % (arf1, arf2)) idxs = hdu.data["TIME"] < sexp mylog.info("Adding %d background events from %s." % (idxs.sum(), bkg_file)) if event_params["roll_angle"] == hdu.header["ROLL_PNT"]: xpix = hdu.data["X"][idxs] ypix = hdu.data["Y"][idxs] else: rot_mat = get_rot_mat(event_params["roll_angle"]) if dither_params["dither_on"]: t = hdu.data["TIME"][idxs] x_off, y_off = perform_dither(t, dither_params) else: x_off = 0.0 y_off = 0.0 det = np.array([ hdu.data["DETX"][idxs] + x_off - event_params["aimpt_coords"][0], hdu.data["DETY"][idxs] + y_off - event_params["aimpt_coords"][1] ]) xpix, ypix = np.dot(rot_mat.T, det) xpix += hdu.header["TCRPX2"] ypix += hdu.header["TCRPX3"] all_events = {} for key in [ "detx", "dety", "time", "ccd_id", event_params["channel_type"] ]: all_events[key] = np.concatenate( [events[key], hdu.data[key.upper()][idxs]]) all_events["xpix"] = np.concatenate([events["xpix"], xpix]) all_events["ypix"] = np.concatenate([events["ypix"], ypix]) all_events["energy"] = np.concatenate( [events["energy"], hdu.data["ENERGY"][idxs] / 1000.0]) f.close() return all_events
def make_exposure_map(event_file, expmap_file, energy, weights=None, asol_file=None, normalize=True, overwrite=False, nhistx=16, nhisty=16): """ Make an exposure map for a SOXS event file, and optionally write an aspect solution file. The exposure map will be created by binning an aspect histogram over the range of the aspect solution. Parameters ---------- event_file : string The path to the event file to use for making the exposure map. expmap_file : string The path to write the exposure map file to. energy : float, (value, unit) tuple, or :class:`~astropy.units.Quantity`, or NumPy array The energy in keV to use when computing the exposure map, or a set of energies to be used with the *weights* parameter. If providing a set, it must be in keV. weights : array-like, optional The weights to use with a set of energies given in the *energy* parameter. Used to create a more accurate exposure map weighted by a range of energies. Default: None asol_file : string, optional The path to write the aspect solution file to, if desired. Default: None normalize : boolean, optional If True, the exposure map will be divided by the exposure time so that the map's units are cm**2. Default: True overwrite : boolean, optional Whether or not to overwrite an existing file. Default: False nhistx : integer, optional The number of bins in the aspect histogram in the DETX direction. Default: 16 nhisty : integer, optional The number of bins in the aspect histogram in the DETY direction. Default: 16 """ import pyregion._region_filter as rfilter from scipy.ndimage.interpolation import rotate, shift from soxs.instrument import AuxiliaryResponseFile, perform_dither if isinstance(energy, np.ndarray) and weights is None: raise RuntimeError("Must supply a single value for the energy if " "you do not supply weights!") if not isinstance(energy, np.ndarray): energy = parse_value(energy, "keV") f_evt = pyfits.open(event_file) hdu = f_evt["EVENTS"] arf = AuxiliaryResponseFile(hdu.header["ANCRFILE"]) exp_time = hdu.header["EXPOSURE"] nx = int(hdu.header["TLMAX2"] - 0.5) // 2 ny = int(hdu.header["TLMAX3"] - 0.5) // 2 ra0 = hdu.header["TCRVL2"] dec0 = hdu.header["TCRVL3"] xdel = hdu.header["TCDLT2"] ydel = hdu.header["TCDLT3"] x0 = hdu.header["TCRPX2"] y0 = hdu.header["TCRPX3"] xdet0 = 0.5 * (2 * nx + 1) ydet0 = 0.5 * (2 * ny + 1) xaim = hdu.header.get("AIMPT_X", 0.0) yaim = hdu.header.get("AIMPT_Y", 0.0) roll = hdu.header["ROLL_PNT"] instr = instrument_registry[hdu.header["INSTRUME"].lower()] dither_params = {} if "DITHXAMP" in hdu.header: dither_params["x_amp"] = hdu.header["DITHXAMP"] dither_params["y_amp"] = hdu.header["DITHYAMP"] dither_params["x_period"] = hdu.header["DITHXPER"] dither_params["y_period"] = hdu.header["DITHYPER"] dither_params["plate_scale"] = ydel * 3600.0 dither_params["dither_on"] = True else: dither_params["dither_on"] = False f_evt.close() # Create time array for aspect solution dt = 1.0 # Seconds t = np.arange(0.0, exp_time + dt, dt) # Construct WCS w = pywcs.WCS(naxis=2) w.wcs.crval = [ra0, dec0] w.wcs.crpix = [x0, y0] w.wcs.cdelt = [xdel, ydel] w.wcs.ctype = ["RA---TAN", "DEC--TAN"] w.wcs.cunit = ["deg"] * 2 # Create aspect solution if we had dithering. # otherwise just set the offsets to zero if dither_params["dither_on"]: x_off, y_off = perform_dither(t, dither_params) # Make the aspect histogram x_amp = dither_params["x_amp"] / dither_params["plate_scale"] y_amp = dither_params["y_amp"] / dither_params["plate_scale"] x_edges = np.linspace(-x_amp, x_amp, nhistx + 1, endpoint=True) y_edges = np.linspace(-y_amp, y_amp, nhisty + 1, endpoint=True) asphist = np.histogram2d(x_off, y_off, (x_edges, y_edges))[0] asphist *= dt x_mid = 0.5 * (x_edges[1:] + x_edges[:-1]) y_mid = 0.5 * (y_edges[1:] + y_edges[:-1]) # Determine the effective area eff_area = arf.interpolate_area(energy).value if weights is not None: eff_area = np.average(eff_area, weights=weights) if instr["chips"] is None: rtypes = ["Box"] args = [[0.0, 0.0, instr["num_pixels"], instr["num_pixels"]]] else: rtypes = [] args = [] for i, chip in enumerate(instr["chips"]): rtypes.append(chip[0]) args.append(np.array(chip[1:])) tmpmap = np.zeros((2 * nx, 2 * ny)) for rtype, arg in zip(rtypes, args): rfunc = getattr(rfilter, rtype) new_args = parse_region_args(rtype, arg, xdet0 - xaim - 1.0, ydet0 - yaim - 1.0) r = rfunc(*new_args) tmpmap += r.mask(tmpmap).astype("float64") if dither_params["dither_on"]: expmap = np.zeros((2 * nx, 2 * ny)) niter = nhistx * nhisty pbar = tqdm(leave=True, total=niter, desc="Creating exposure map ") for i in range(nhistx): for j in range(nhisty): expmap += shift(tmpmap, (x_mid[i], y_mid[j]), order=0) * asphist[i, j] pbar.update(nhisty) pbar.close() else: expmap = tmpmap * exp_time expmap *= eff_area if normalize: expmap /= exp_time if roll != 0.0: rotate(expmap, roll, output=expmap, reshape=False) map_header = { "EXPOSURE": exp_time, "MTYPE1": "EQPOS", "MFORM1": "RA,DEC", "CTYPE1": "RA---TAN", "CTYPE2": "DEC--TAN", "CRVAL1": ra0, "CRVAL2": dec0, "CUNIT1": "deg", "CUNIT2": "deg", "CDELT1": xdel, "CDELT2": ydel, "CRPIX1": x0, "CRPIX2": y0 } map_hdu = pyfits.ImageHDU(expmap, header=pyfits.Header(map_header)) map_hdu.name = "EXPMAP" map_hdu.writeto(expmap_file, overwrite=overwrite) if asol_file is not None: if dither_params["dither_on"]: det = np.array([x_off, y_off]) pix = np.dot(get_rot_mat(roll).T, det) ra, dec = w.wcs_pix2world(pix[0, :] + x0, pix[1, :] + y0, 1) col_t = pyfits.Column(name='time', format='D', unit='s', array=t) col_ra = pyfits.Column(name='ra', format='D', unit='deg', array=ra) col_dec = pyfits.Column(name='dec', format='D', unit='deg', array=dec) coldefs = pyfits.ColDefs([col_t, col_ra, col_dec]) tbhdu = pyfits.BinTableHDU.from_columns(coldefs) tbhdu.name = "ASPSOL" tbhdu.header["EXPOSURE"] = exp_time hdulist = [pyfits.PrimaryHDU(), tbhdu] pyfits.HDUList(hdulist).writeto(asol_file, overwrite=overwrite) else: mylog.warning("Refusing to write an aspect solution file because " "there was no dithering.")
def add_background_from_file(events, event_params, bkg_file): from soxs.instrument import perform_dither f = pyfits.open(bkg_file) hdu = f["EVENTS"] dither_params = {} if "DITHXAMP" in hdu.header: dither_params["x_amp"] = hdu.header["DITHXAMP"] dither_params["y_amp"] = hdu.header["DITHYAMP"] dither_params["x_period"] = hdu.header["DITHXPER"] dither_params["y_period"] = hdu.header["DITHYPER"] dither_params["plate_scale"] = hdu.header["TCDLT3"]*3600.0 dither_params["dither_on"] = True else: dither_params["dither_on"] = False sexp = event_params["exposure_time"] bexp = hdu.header["EXPOSURE"] if event_params["exposure_time"] > hdu.header["EXPOSURE"]: raise RuntimeError("The background file does not have sufficient exposure! Source " "exposure time %g, background exposure time %g." % (sexp, bexp)) for k1, k2 in key_map.items(): if event_params[k1] != hdu.header[k2]: raise RuntimeError("'%s' keyword does not match! %s vs. %s" % (k1, event_params[k1], hdu.header[k2])) rmf1 = os.path.split(event_params["rmf"])[-1] rmf2 = hdu.header["RESPFILE"] arf1 = os.path.split(event_params["arf"])[-1] arf2 = hdu.header["ANCRFILE"] if rmf1 != rmf2: raise RuntimeError("RMFs do not match! %s vs. %s" % (rmf1, rmf2)) if arf1 != arf2: raise RuntimeError("ARFs do not match! %s vs. %s" % (arf1, arf2)) idxs = hdu.data["TIME"] < sexp mylog.info("Adding %d background events from %s." % (idxs.sum(), bkg_file)) if event_params["roll_angle"] == hdu.header["ROLL_PNT"]: xpix = hdu.data["X"][idxs] ypix = hdu.data["Y"][idxs] else: rot_mat = get_rot_mat(event_params["roll_angle"]) if dither_params["dither_on"]: t = hdu.data["TIME"][idxs] x_off, y_off = perform_dither(t, dither_params) else: x_off = 0.0 y_off = 0.0 det = np.array([hdu.data["DETX"][idxs] + x_off - event_params["aimpt_coords"][0], hdu.data["DETY"][idxs] + y_off - event_params["aimpt_coords"][1]]) xpix, ypix = np.dot(rot_mat.T, det) xpix += hdu.header["TCRPX2"] ypix += hdu.header["TCRPX3"] all_events = {} for key in ["detx", "dety", "time", "ccd_id", event_params["channel_type"]]: all_events[key] = np.concatenate([events[key], hdu.data[key.upper()][idxs]]) all_events["xpix"] = np.concatenate([events["xpix"], xpix]) all_events["ypix"] = np.concatenate([events["ypix"], ypix]) all_events["energy"] = np.concatenate([events["energy"], hdu.data["ENERGY"][idxs]/1000.0]) f.close() return all_events
def make_exposure_map(event_file, expmap_file, energy, weights=None, asol_file=None, normalize=True, overwrite=False, reblock=1, nhistx=16, nhisty=16, order=1): """ Make an exposure map for a SOXS event file, and optionally write an aspect solution file. The exposure map will be created by binning an aspect histogram over the range of the aspect solution. Parameters ---------- event_file : string The path to the event file to use for making the exposure map. expmap_file : string The path to write the exposure map file to. energy : float, (value, unit) tuple, or :class:`~astropy.units.Quantity`, or NumPy array The energy in keV to use when computing the exposure map, or a set of energies to be used with the *weights* parameter. If providing a set, it must be in keV. weights : array-like, optional The weights to use with a set of energies given in the *energy* parameter. Used to create a more accurate exposure map weighted by a range of energies. Default: None asol_file : string, optional The path to write the aspect solution file to, if desired. Default: None normalize : boolean, optional If True, the exposure map will be divided by the exposure time so that the map's units are cm**2. Default: True overwrite : boolean, optional Whether or not to overwrite an existing file. Default: False reblock : integer, optional Supply an integer power of 2 here to make an exposure map with a different binning. Default: 1 nhistx : integer, optional The number of bins in the aspect histogram in the DETX direction. Default: 16 nhisty : integer, optional The number of bins in the aspect histogram in the DETY direction. Default: 16 order : integer, optional The interpolation order to use when making the exposure map. Default: 1 """ import pyregion._region_filter as rfilter from scipy.ndimage.interpolation import rotate, shift from soxs.instrument import AuxiliaryResponseFile, perform_dither if isinstance(energy, np.ndarray) and weights is None: raise RuntimeError("Must supply a single value for the energy if " "you do not supply weights!") if not isinstance(energy, np.ndarray): energy = parse_value(energy, "keV") f_evt = pyfits.open(event_file) hdu = f_evt["EVENTS"] arf = AuxiliaryResponseFile(hdu.header["ANCRFILE"]) exp_time = hdu.header["EXPOSURE"] nx = int(hdu.header["TLMAX2"]-0.5)//2 ny = int(hdu.header["TLMAX3"]-0.5)//2 ra0 = hdu.header["TCRVL2"] dec0 = hdu.header["TCRVL3"] xdel = hdu.header["TCDLT2"] ydel = hdu.header["TCDLT3"] x0 = hdu.header["TCRPX2"] y0 = hdu.header["TCRPX3"] xdet0 = 0.5*(2*nx+1) ydet0 = 0.5*(2*ny+1) xaim = hdu.header.get("AIMPT_X", 0.0) yaim = hdu.header.get("AIMPT_Y", 0.0) roll = hdu.header["ROLL_PNT"] instr = instrument_registry[hdu.header["INSTRUME"].lower()] dither_params = {} if "DITHXAMP" in hdu.header: dither_params["x_amp"] = hdu.header["DITHXAMP"] dither_params["y_amp"] = hdu.header["DITHYAMP"] dither_params["x_period"] = hdu.header["DITHXPER"] dither_params["y_period"] = hdu.header["DITHYPER"] dither_params["plate_scale"] = ydel*3600.0 dither_params["dither_on"] = True else: dither_params["dither_on"] = False f_evt.close() # Create time array for aspect solution dt = 1.0 # Seconds t = np.arange(0.0, exp_time+dt, dt) # Construct WCS w = pywcs.WCS(naxis=2) w.wcs.crval = [ra0, dec0] w.wcs.crpix = [x0, y0] w.wcs.cdelt = [xdel, ydel] w.wcs.ctype = ["RA---TAN","DEC--TAN"] w.wcs.cunit = ["deg"]*2 # Create aspect solution if we had dithering. # otherwise just set the offsets to zero if dither_params["dither_on"]: x_off, y_off = perform_dither(t, dither_params) # Make the aspect histogram x_amp = dither_params["x_amp"]/dither_params["plate_scale"] y_amp = dither_params["y_amp"]/dither_params["plate_scale"] x_edges = np.linspace(-x_amp, x_amp, nhistx+1, endpoint=True) y_edges = np.linspace(-y_amp, y_amp, nhisty+1, endpoint=True) asphist = np.histogram2d(x_off, y_off, (x_edges, y_edges))[0] asphist *= dt x_mid = 0.5*(x_edges[1:]+x_edges[:-1])/reblock y_mid = 0.5*(y_edges[1:]+y_edges[:-1])/reblock # Determine the effective area eff_area = arf.interpolate_area(energy).value if weights is not None: eff_area = np.average(eff_area, weights=weights) if instr["chips"] is None: rtypes = ["Box"] args = [[0.0, 0.0, instr["num_pixels"], instr["num_pixels"]]] else: rtypes = [] args = [] for i, chip in enumerate(instr["chips"]): rtypes.append(chip[0]) args.append(np.array(chip[1:])) tmpmap = np.zeros((2*nx, 2*ny)) for rtype, arg in zip(rtypes, args): rfunc = getattr(rfilter, rtype) new_args = parse_region_args(rtype, arg, xdet0-xaim-1.0, ydet0-yaim-1.0) r = rfunc(*new_args) tmpmap += r.mask(tmpmap).astype("float64") tmpmap = downsample(tmpmap, reblock) if dither_params["dither_on"]: expmap = np.zeros(tmpmap.shape) niter = nhistx*nhisty pbar = tqdm(leave=True, total=niter, desc="Creating exposure map ") for i in range(nhistx): for j in range(nhisty): expmap += shift(tmpmap, (x_mid[i], y_mid[j]), order=order)*asphist[i, j] pbar.update(nhisty) pbar.close() else: expmap = tmpmap*exp_time expmap *= eff_area if normalize: expmap /= exp_time if roll != 0.0: rotate(expmap, roll, output=expmap, reshape=False) expmap[expmap < 0.0] = 0.0 map_header = {"EXPOSURE": exp_time, "MTYPE1": "EQPOS", "MFORM1": "RA,DEC", "CTYPE1": "RA---TAN", "CTYPE2": "DEC--TAN", "CRVAL1": ra0, "CRVAL2": dec0, "CUNIT1": "deg", "CUNIT2": "deg", "CDELT1": xdel*reblock, "CDELT2": ydel*reblock, "CRPIX1": 0.5*(2.0*nx//reblock+1), "CRPIX2": 0.5*(2.0*ny//reblock+1)} map_hdu = pyfits.ImageHDU(expmap, header=pyfits.Header(map_header)) map_hdu.name = "EXPMAP" map_hdu.writeto(expmap_file, overwrite=overwrite) if asol_file is not None: if dither_params["dither_on"]: det = np.array([x_off, y_off]) pix = np.dot(get_rot_mat(roll).T, det) ra, dec = w.wcs_pix2world(pix[0,:]+x0, pix[1,:]+y0, 1) col_t = pyfits.Column(name='time', format='D', unit='s', array=t) col_ra = pyfits.Column(name='ra', format='D', unit='deg', array=ra) col_dec = pyfits.Column(name='dec', format='D', unit='deg', array=dec) coldefs = pyfits.ColDefs([col_t, col_ra, col_dec]) tbhdu = pyfits.BinTableHDU.from_columns(coldefs) tbhdu.name = "ASPSOL" tbhdu.header["EXPOSURE"] = exp_time hdulist = [pyfits.PrimaryHDU(), tbhdu] pyfits.HDUList(hdulist).writeto(asol_file, overwrite=overwrite) else: mylog.warning("Refusing to write an aspect solution file because " "there was no dithering.")
def make_uniform_background(energy, event_params, rmf, prng=None): from soxs.instrument import perform_dither import pyregion._region_filter as rfilter prng = parse_prng(prng) bkg_events = {} n_events = energy.size nx = event_params["num_pixels"] bkg_events["detx"] = prng.uniform(low=-0.5 * nx, high=0.5 * nx, size=n_events) bkg_events["dety"] = prng.uniform(low=-0.5 * nx, high=0.5 * nx, size=n_events) bkg_events["energy"] = energy if event_params["chips"] is None: bkg_events["chip_id"] = np.zeros(n_events, dtype='int') else: bkg_events["chip_id"] = -np.ones(n_events, dtype='int') for i, chip in enumerate(event_params["chips"]): thisc = np.ones(n_events, dtype='bool') rtype = chip[0] args = chip[1:] r = getattr(rfilter, rtype)(*args) inside = r.inside(bkg_events["detx"], bkg_events["dety"]) thisc = np.logical_and(thisc, inside) bkg_events["chip_id"][thisc] = i keep = bkg_events["chip_id"] > -1 for key in bkg_events: bkg_events[key] = bkg_events[key][keep] n_e = bkg_events["energy"].size bkg_events['time'] = prng.uniform(size=n_e, low=0.0, high=event_params["exposure_time"]) x_offset, y_offset = perform_dither(bkg_events["time"], event_params["dither_params"]) rot_mat = get_rot_mat(event_params["roll_angle"]) det = np.array([ bkg_events["detx"] + x_offset - event_params["aimpt_coords"][0], bkg_events["dety"] + y_offset - event_params["aimpt_coords"][1] ]) pix = np.dot(rot_mat.T, det) bkg_events["xpix"] = pix[0, :] + event_params['pix_center'][0] bkg_events["ypix"] = pix[1, :] + event_params['pix_center'][1] mylog.info("Scattering energies with RMF %s." % os.path.split(rmf.filename)[-1]) bkg_events = rmf.scatter_energies(bkg_events, prng=prng) return bkg_events