class AuxiliaryResponseFile(object): r""" A class for auxiliary response files (ARFs). Parameters ---------- filename : string The filename of the ARF to be read. rmffile : string The filename of a redistribution matrix file (RMF) to be read. This is sometimes needed because some ARFs are unnormalized. Examples -------- >>> arf = AuxiliaryResponseFile("sxt-s_120210_ts02um_intallpxl.arf", ... rmffile="ah_sxs_5ev_basefilt_20100712.rmf") """ def __init__(self, filename, rmffile=None): self.filename = check_file_location(filename, "response_files") f = _astropy.pyfits.open(self.filename) self.elo = YTArray(f["SPECRESP"].data.field("ENERG_LO"), "keV") self.ehi = YTArray(f["SPECRESP"].data.field("ENERG_HI"), "keV") self.emid = 0.5 * (self.elo + self.ehi) self.eff_area = YTArray( np.nan_to_num(f["SPECRESP"].data.field("SPECRESP")), "cm**2") if rmffile is not None: rmf = RedistributionMatrixFile(rmffile) self.eff_area *= rmf.weights self.rmffile = rmf.filename else: mylog.warning( "You specified an ARF but not an RMF. This is ok if you know " "a priori that the responses are normalized properly. If not, " "you may get inconsistent results.") f.close() def __str__(self): return self.filename def detect_events(self, energy, area, prng=None): """ Use the ARF to determine a subset of photons which will be detected. Returns a boolean NumPy array which is the same is the same size as the number of photons, wherever it is "true" means those photons have been detected. Parameters ---------- energy : :class:`~yt.units.yt_array.YTArray` The energies of the photons to attempt to detect, in keV. area : float, tuple, or YTQuantity The collecting area associated with the event energies. If a floating-point number, is assumed to be in cm^2. prng : :class:`~numpy.random.RandomState` object or :mod:`~numpy.random`, optional 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 the :mod:`~numpy.random` module. """ if prng is None: prng = np.random earea = np.interp(energy, self.emid, self.eff_area, left=0.0, right=0.0) randvec = area.v * prng.uniform(size=energy.shape) return randvec < earea @property def max_area(self): return self.eff_area.max() def interpolate_area(self, energy): """ Interpolate the effective area to the energies provided by the supplied *energy* array. """ earea = np.interp(energy, self.emid, self.eff_area, left=0.0, right=0.0) return YTArray(earea, "cm**2")