def make_rsp(self, wave): '''Interpolate the COS LSF for all wavelengths Parameters ---------- wave : ndarray wavelength (assumed to be in Ang) ''' # test if delta_lambda in wavelengths is as expected for this grating for disp in self.disp: if max(abs(np.diff(wave) - disp)) < (disp / 100.): break else: print('LSF convolution requires x to be binned in pixels.') print('delta(x) differs from value given in COS IHB.') # first line of table is header values wavelen = self.lsf_tab[0, 1:] if ((min(wave) < 0.9 * min(wavelen)) or (max(wave) > 1.1 * max(wavelen))): raise ValueError('COS LSF in this model is only tabulated {0:4.0f} to {1:0.0f}'.format(min(wavelen), max(wavelen))) # rest of table is data values # 1. Interpolation: Get lsf_tab on pixel grid # interpolate to full pixels, if given in fractional pixels lsftab = np.zeros((self.m, len(wavelen))) for i in range(len(wavelen)): lsftab[:,i] = interpolate(np.arange(-(self.m/2), self.m/2+0.1, dtype = np.int), self.shift, self.lsf_tab[1:,i+1]) # 2. Interpolation: make lsf for each lambda n = len(wave) self._rsp = np.zeros((n, self.m)) for i in np.arange(-(self.m/2), self.m/2+0.1, dtype = np.int): pix = interpolate(wave, wavelen, lsftab[self.m/2+i,:]) self._rsp[:, self.m/2 + i] = pix if np.isfinite(self._rsp).sum() != self._rsp.size: raise ValueError('Response matrix holds nan or inf values!') # 3. Reformat: Bring matix in same shape as Sherpa would do for RMFs # I use the same naming convention here as in Sherpa `DataRMF` objects self._grp = np.ones(n) self._fch = np.clip(np.arange(1,n+1)-(self.m/2),1, np.inf) # set unused matix elements to nan - that allows a very simple # way to flatten the array for i in range(self.m/2): self._rsp[i,0:self.m/2-i] = np.nan self._rsp[-(i+1),-(self.m/2)+i:self.m] = np.nan self._nch = np.sum(np.isfinite(self._rsp),axis = 1) self._rsp = self._rsp[np.isfinite(self._rsp)].flatten() self.cache_x = wave
def _evaluate(self, data_space, pars, modelfunc, **kwargs): # Evaluate the model on the user-defined grid and then interpolate/rebin # onto the desired grid. This is based on sherpa.models.TableModel # but is simplified as we do not provide a fold method. kwargs[ 'integrate'] = self.integrate # Not really sure I need this, but let's be safe evaluation_space = self.evaluation_space if not data_space in evaluation_space: warnings.warn( "evaluation space does not contain the requested space. Sherpa will join the two spaces." ) evaluation_space = evaluation_space.join(data_space) # I don't like the string of IFs, but it might be more expressive this way in this specific case. # If the data space is integrated and the model's integrate flag is set to True, then evaluate the model # on the evaluation space and then rebin onto the data space. # If the data space is integrated but the model's integrate flas is set to False, then evaluate the model # on the midpoint grid (note: we are passing the midpoint grid to force Sherpa to treat this as not integrated. # If we passed two arrays we'd fall in a edge case and Sherpa would evaluate the model at the edge of the bin. # If the data space is not integrated then simply evaluate the model on the grid and then interpolate # to match the data space. if data_space.is_integrated: if self.integrate: # This should be the most common case y = modelfunc(pars, evaluation_space.grid[0], evaluation_space.grid[1], **kwargs) return rebin(y, evaluation_space.grid[0], evaluation_space.grid[1], data_space.grid[0], data_space.grid[1]) else: # The integrate flag is set to false, so just evaluate the model # and then interpolate using the grids midpoints. y = modelfunc(pars, evaluation_space.midpoint_grid, **kwargs) return interpolate(data_space.midpoint_grid, evaluation_space.midpoint_grid, y, function=self.method) else: y = modelfunc(pars, evaluation_space.grid[0], **kwargs) return interpolate(data_space.midpoint_grid, evaluation_space.midpoint_grid, y, function=self.method)
def calc(self, pl, pr, rhs, *args, **kwargs): args = list(args) # tuples are immutable! # add m/2 elements to the wavelength array on each side # to avoid edge effects when folding with the LSF args[0] = interpolate(np.arange(-(self.m/2), len(args[0]) + self.m/2-0.1), np.arange(len(args[0])), args[0]) flux = rhs(pr, *args, **kwargs) return self.convolve(args[0], flux)[self.m/2 : -(self.m/2)]
def _evaluate(self, data_space, pars, modelfunc, **kwargs): # Evaluate the model on the user-defined grid and then interpolate/rebin # onto the desired grid. This is based on sherpa.models.TableModel # but is simplified as we do not provide a fold method. kwargs['integrate'] = self.integrate # Not really sure I need this, but let's be safe evaluation_space = self.evaluation_space if not data_space in evaluation_space: warnings.warn("evaluation space does not contain the requested space. Sherpa will join the two spaces.") evaluation_space = evaluation_space.join(data_space) # I don't like the string of IFs, but it might be more expressive this way in this specific case. # If the data space is integrated and the model's integrate flag is set to True, then evaluate the model # on the evaluation space and then rebin onto the data space. # If the data space is integrated but the model's integrate flas is set to False, then evaluate the model # on the midpoint grid (note: we are passing the midpoint grid to force Sherpa to treat this as not integrated. # If we passed two arrays we'd fall in a edge case and Sherpa would evaluate the model at the edge of the bin. # If the data space is not integrated then simply evaluate the model on the grid and then interpolate # to match the data space. if data_space.is_integrated: if self.integrate: # This should be the most common case y = modelfunc(pars, evaluation_space.grid[0], evaluation_space.grid[1], **kwargs) return rebin(y, evaluation_space.grid[0], evaluation_space.grid[1], data_space.grid[0], data_space.grid[1]) else: # The integrate flag is set to false, so just evaluate the model # and then interpolate using the grids midpoints. y = modelfunc(pars, evaluation_space.midpoint_grid, **kwargs) return interpolate(data_space.midpoint_grid, evaluation_space.midpoint_grid, y, function=self.method) else: y = modelfunc(pars, evaluation_space.grid, **kwargs) return interpolate(data_space.midpoint_grid, evaluation_space.midpoint_grid, y, function=self.method)
def _estimate_expmap(self, *args): """Estimate the exposure map given an ARF. Although the arguments are listed with parameter names below, the function **does not** accept named arguments. It uses positional arguments and type checks to determine the parameters. Parameters ---------- crate A TABLECrate, containing ``energ_lo``, ``energ_hi``, and ``specresp`` columns. filename : string The name of an ARF file xlo, xhi, y : arrays of numbers The arrays taken to be the ``energ_lo``, ``energ_hi``, and ``specresp`` columns Return ------ expmap : number An estimate of the exposure map at the position of the source, and has units of cm^2 count / self.fluxtype Notes ----- The ARF is linearly interpolated onto the energy grid of the dataset and the weighted sum calculated. The ARF is assumed to have units of cm^2 count / photon, and be defined on a grid given in keV. The ``estimate_instmap()`` method should be called in preference to this routine. """ nargs = len(args) if nargs == 3: elo = args[0] ehi = args[1] specresp = args[2] if len(elo) == 1 or len(ehi) == 1 or len(specresp) == 1: emsg = "Expected three arrays of the same " + \ "length, with more than one element in" raise TypeError(emsg) if len(elo) != len(ehi) or len(elo) != len(specresp): emsg = "Expected three arrays of the same length" raise ValueError(emsg) elif nargs != 1: emsg = "_estimate_expmap() takes 2 or 4 arguments " + \ "({} given)".format(nargs + 1) raise TypeError(emsg) else: if isinstance(args[0], pycrates.TABLECrate): cr = args[0] else: cr = pycrates.TABLECrate(args[0], mode="r") try: elo = cr.get_column("ENERG_LO").values.copy() ehi = cr.get_column("ENERG_HI").values.copy() specresp = cr.get_column("SPECRESP").values.copy() except ValueError as e: fname = cr.get_filename() raise ValueError("Crate {} - {}".format(fname, e)) # Interpolate using the mid-point of the ARF # onto the mid-point of the grid if necessary. # emid = 0.5 * (elo + ehi) if emid.shape == self.xmid.shape and \ np.all(emid == self.xmid): arf = specresp else: arf = su.interpolate(self.xmid, emid, specresp) arf = np.asarray(arf, dtype=self.weight.dtype) arf[arf < 0] = 0.0 return np.sum(self.weight * arf)