1) # misnomer ... this is really the integral self.r0 = Parameter(name, 'r0', 1, 0) self.width = Parameter(name, 'width', 0.1, 0) ArithmeticModel.__init__( self, name, (self.xpos, self.ypos, self.ampl, self.r0, self.width)) def calc(self, p, x, y, *args, **kwargs): """Homogeneously emitting spherical shell, projected along the z-direction (this is not 100% correct for very large shells on the sky).""" xpos, ypos, ampl, r_0, width = p r2 = (x - xpos) * (x - xpos) + (y - ypos) * (y - ypos) r_out = r_0 + width r_in2, r_out2 = r_0 * r_0, r_out * r_out # r_in3, r_out3 = r_in * r_in2, r_out * r_out2 # We only call abs() in sqrt() to avoid warning messages. sphere_out = np.sqrt(np.abs(r_out2 - r2)) sphere_in = np.sqrt(np.abs(r_in2 - r2)) # Note: for r > r_out 'np.select' fills automatically zeros! non_normalized = np.select([r2 <= r_in2, r2 <= r_out2], [sphere_out - sphere_in, sphere_out]) # Computed with Mathematica: integral = 2 * np.pi / 3 * (r_out**3 - r_0**3) # integral = 1 return ampl * non_normalized / integral sau.add_model(normdisk2d) sau.add_model(normshell2d)
self.xpos = Parameter(name, 'xpos', 0) # p[0] self.ypos = Parameter(name, 'ypos', 0) # p[1] self.ampl = Parameter(name, 'ampl', 1) # p[2] self.r0 = Parameter(name, 'r0', 1, 0) # p[3] ArithmeticModel.__init__(self, name, (self.xpos, self.ypos, self.ampl, self.r0)) def calc(self, p, x, y, *args, **kwargs): # Compute radii r2 = (x - p[0])**2 + (y - p[1])**2 # Return ampl when r2 <= r0 else return 0 return np.select([r2 <= p[3]**2], [p[2]]) sau.add_model(disk2d) class shell2d(ArithmeticModel): def __init__(self, name='shell2d'): self.xpos = Parameter(name, 'xpos', 0) # p[0] self.ypos = Parameter(name, 'ypos', 0) # p[1] self.ampl = Parameter(name, 'ampl', 1) # p[2] self.r0 = Parameter(name, 'r0', 1, 0) # p[3] self.width = Parameter(name, 'width', 0.1, 0) ArithmeticModel.__init__( self, name, (self.xpos, self.ypos, self.ampl, self.r0, self.width)) def calc(self, p, x, y, *args, **kwargs): """Homogeneously emitting spherical shell, projected along the z-direction
layer. The added models are reported to the user using Sherpa's logger at the logging.INFO level. If the model exists in Sherpa's XSPEC module then we skip it here. """ import logging from sherpa.astro import ui from sherpa.astro import xspec import xspeclmodels logger = logging.getLogger('sherpa') # For now process all models (including convolution style) the # same way. # xsmodels = set([n for n in dir(xspec) if n.startswith('XS')]) for name in xspeclmodels.__all__: if not name.startswith('XS') or name in xsmodels: continue cls = getattr(xspeclmodels, name) ui.add_model(cls) logger.info("Adding XSPEC local model: {}".format(name.lower()))
if self._cur_cache_key != cache_key: self._cached_vals = [None] * self._kt_array.size for i in range(self._kt_array.size): apec_params = [self._kt_array[i], abund, redshift, 1.] self._cached_vals[i] = _xspec.xsaped(apec_params, *args, **kwargs) self._cur_cache_key = cache_key self._cached_vals = np.array(self._cached_vals).T scales = norm * self._kt_array**gfac return (self._cached_vals * scales).sum(axis=1) ui.add_model(PowerLawApecDemModel) def make_fixed_temp_multi_apec(kTs, name_template='apec%d', norm=None): """Create a model summing multiple APEC components at fixed temperatures. *kTs* An iterable of temperatures for the components, in keV. *name_template* = 'apec%d' A template to use for the names of each component; it is string-formatted with the 0-based component number as an argument. *norm* = None An initial normalization to be used for every component, or None to use the Sherpa default. Returns: A tuple ``(total_model, sub_models)``, where *total_model* is a Sherpa
self.ypos = Parameter(name, 'ypos', 0) self.ampl = Parameter(name, 'ampl', 1) # misnomer ... this is really the integral self.r0 = Parameter(name, 'r0', 1, 0) self.width = Parameter(name, 'width', 0.1, 0) ArithmeticModel.__init__(self, name, (self.xpos, self.ypos, self.ampl, self.r0, self.width)) def calc(self, p, x, y, *args, **kwargs): """Homogeneously emitting spherical shell, projected along the z-direction (this is not 100% correct for very large shells on the sky).""" xpos, ypos, ampl, r_0, width = p r2 = (x - xpos) * (x - xpos) + (y - ypos) * (y - ypos) r_out = r_0 + width r_in2, r_out2 = r_0 * r_0, r_out * r_out # r_in3, r_out3 = r_in * r_in2, r_out * r_out2 # We only call abs() in sqrt() to avoid warning messages. sphere_out = np.sqrt(np.abs(r_out2 - r2)) sphere_in = np.sqrt(np.abs(r_in2 - r2)) # Note: for r > r_out 'np.select' fills automatically zeros! non_normalized = np.select([r2 <= r_in2, r2 <= r_out2], [sphere_out - sphere_in, sphere_out]) # Computed with Mathematica: integral = 2 * np.pi / 3 * (r_out ** 3 - r_0 ** 3) # integral = 1 return ampl * non_normalized / integral sau.add_model(normdisk2d) sau.add_model(normshell2d)
ArithmeticModel.__init__(self, name, (self.xpos, self.ypos, self.ampl, self.fwhm)) def set_wcs(self, wcs): self.wcs = wcs # We assume bins have the same size along x and y axis self.binsize = np.abs(self.wcs.wcs.cdelt[0]) if self.wcs.wcs.ctype[0][0:4] == 'GLON': self.coordsys = 'galactic' elif self.wcs.wcs.ctype[0][0:2] == 'RA': self.coordsys = 'fk5' # print self.coordsys def calc(self, p, xlo, xhi, ylo, yhi, *args, **kwargs): """ The normgauss2dint model uses the error function to evaluate the the gaussian. This corresponds to an integration over bins. """ return self.normgauss2d(p, xlo, xhi, ylo, yhi) def normgauss2d(self, p, xlo, xhi, ylo, yhi): sigma_erf = p[3] * fwhm_to_sigma_erf return p[2] / 4. * ((erf.calc.calc([1, p[0], sigma_erf], xhi) - erf.calc.calc([1, p[0], sigma_erf], xlo)) * (erf.calc.calc([1, p[1], sigma_erf], yhi) - erf.calc.calc([1, p[1], sigma_erf], ylo))) sau.add_model(NormGauss2DInt)