def image_model_sherpa(exposure, psf, sources, model_image, overwrite): """Compute source model image with Sherpa. Inputs: * Source list (JSON file) * PSF (JSON file) * Exposure image (FITS file) Outputs: * Source model flux image (FITS file) * Source model excess image (FITS file) """ import sherpa.astro.ui as sau from ..image.models.psf import Sherpa from ..image.models.utils import read_json log.info('Reading exposure: {0}'.format(exposure)) # Note: We don't really need the exposure as data, # but this is a simple way to init the dataspace to the correct shape sau.load_data(exposure) sau.load_table_model('exposure', exposure) log.info('Reading PSF: {0}'.format(psf)) Sherpa(psf).set() log.info('Reading sources: {0}'.format(sources)) read_json(sources, sau.set_source) name = sau.get_source().name full_model = 'exposure * psf({})'.format(name) sau.set_full_model(full_model) log.info('Computing and writing model_image: {0}'.format(model_image)) sau.save_model(model_image, clobber=overwrite) sau.clean() sau.delete_psf()
def sherpa_model_image(exposure, psf, sources, model_image, overwrite): """Compute source model image with Sherpa. Inputs: * Source list (JSON file) * PSF (JSON file) * Exposure image (FITS file) Outputs: * Source model flux image (FITS file) * Source model excess image (FITS file) """ import logging logging.basicConfig(level=logging.DEBUG, format='%(levelname)s - %(message)s') import sherpa.astro.ui as sau # @UnresolvedImport from ..morphology.psf import Sherpa from ..morphology.utils import read_json logging.info('Reading exposure: {0}'.format(exposure)) # Note: We don't really need the exposure as data, # but this is a simple way to init the dataspace to the correct shape sau.load_data(exposure) sau.load_table_model('exposure', exposure) logging.info('Reading PSF: {0}'.format(psf)) Sherpa(psf).set() logging.info('Reading sources: {0}'.format(sources)) read_json(sources, sau.set_source) name = sau.get_source().name full_model = 'exposure * psf({})'.format(name) sau.set_full_model(full_model) logging.info('Computing and writing model_image: {0}'.format(model_image)) sau.save_model(model_image, clobber=overwrite)
# pure python verstion of single_gaussian_component_model.ipynb because this is faster import sherpa.astro.ui as shp import utils shp.set_stat('cash') shp.set_method('simplex') shp.set_method_opt('verbose', 1) utils.setup('numosaic_fin_combined_40-50keV.img') utils.load_components('40-50 keV/bg 150-212.5.model') # CHXE model shp.set_full_model(psf(shp.gauss2d.chxe * emap) + bg * emap) # inner radii chosen for r in [20, 24, 28, 32]: # from KP15, (ra, dec) = (266.4172 deg, -29.00716 deg) # systematic shifting error = 6" = 2.4 px # ellip = .52 # theta = 57 deg (from positive north) shp.set_par(chxe.xpos, 497.4, 497.4 - 4, 497.4 + 4) shp.set_par(chxe.ypos, 499.1, 499.1 - 4, 499.1 + 4) shp.set_par(chxe.fwhm, 30, 1, 200) shp.set_par(chxe.ellip, .5) shp.set_par(chxe.theta, -.9948) shp.set_par(chxe.ampl, 1e-5) shp.thaw(chxe.ellip, chxe.xpos, chxe.ypos) print(shp.get_model())
sh.set_method("simplex") sh.load_image("analysis_3d/counts_2D.fits") sh.set_coord("logical") sh.load_table_model("expo", "analysis_3d/exposure_2D.fits") sh.load_table_model("bkg", "analysis_3d/background_2D.fits") sh.load_psf("psf", "analysis_3d/psf_2D.fits") # In principle one might first want to fit the background amplitude. However the background estimation method already yields the correct normalization, so we freeze the background amplitude to unity instead of adjusting it. The (smoothed) residuals from this background model are then computed and shown. # In[ ]: sh.set_full_model(bkg) bkg.ampl = 1 sh.freeze(bkg) # In[ ]: resid = Map.read("analysis_3d/counts_2D.fits") resid.data = sh.get_data_image().y - sh.get_model_image().y resid_smooth = resid.smooth(width=4) resid_smooth.plot(add_cbar=True); # ### Find and fit the brightest source # We then find the position of the maximum in the (smoothed) residuals map, and fit a (symmetrical) Gaussian source with that initial position:
# In[53]: import sherpa.astro.ui as sh sh.set_stat("cash") sh.set_method("simplex") sh.load_image('../datasets/images/MSH15-52_counts.fits.gz') sh.set_coord("logical") sh.load_table_model("expo", "../datasets/images/MSH15-52_exposure.fits.gz") sh.load_table_model("bkg", "../datasets/images/MSH15-52_background.fits.gz") sh.load_psf("psf", "../datasets/images/MSH15-52_psf.fits.gz") # In[54]: sh.set_full_model(bkg) bkg.ampl = 1 sh.freeze(bkg) data = sh.get_data_image().y - sh.get_model_image().y resid = SkyImage(data=data, wcs=ref_image.wcs) resid_table = [] #Keep residual images in a list to show them later resid_smo6 = resid.smooth(radius=6) resid_smo6.plot(vmax=5, add_cbar=True) resid_table.append(resid_smo6) # In[55]: maxcoord = resid_smo6.lookup_max() maxpix = resid_smo6.wcs_skycoord_to_pixel(maxcoord[0])
prefix += 'zfree_' parameters += [redshift] priors += [priorfuncs.create_uniform_prior_for(redshift)] assert len(priors) == len( parameters), 'priors: %d parameters: %d' % (len(priors), len(parameters)) ################ # set model # find background automatically using PCA method print('setting source and background model ...') set_model(id, model * galabso) convmodel = get_model(id) bkg_model = auto_background(id) set_full_model(id, get_response(id)(model) + bkg_model * get_bkg_scale(id)) #plot_bkg_fit(id) ## we allow the background normalisation to be a free fitting parameter p = bkg_model.pars[0] p.max = p.val + 2 p.min = p.val - 2 parameters.append(p) priors += [priorfuncs.create_uniform_prior_for(p)] priorfunction = priorfuncs.create_prior_function(priors=priors) assert not numpy.isnan( calc_stat(id)), 'NaN on calc_stat, probably a bad RMF/ARF file for PC' print('running BXA ...')
def fit(self): # try a PCA decomposition of this spectrum initial = self.decompose() ui.set_method('neldermead') bkgmodel = PCAModel('pca%s' % self.id, data=self.pca) self.bkgmodel = bkgmodel response = get_identity_response(self.id) convbkgmodel = response(bkgmodel) ui.set_bkg_full_model(self.id, convbkgmodel) for p, v in zip(bkgmodel.pars, initial): p.val = v srcmodel = ui.get_model(self.id) ui.set_full_model(self.id, srcmodel) initial_v = self.calc_bkg_stat() # print('before fit: stat: %s' % (initial_v)) ui.fit_bkg(id=self.id) # print('fit: first full fit done') final = [p.val for p in ui.get_bkg_model(self.id).pars] # print('fit: parameters: %s' % (final)) initial_v = self.calc_bkg_stat() # print('fit: stat: %s' % (initial_v)) # lets try from zero # logf.info('fit: second full fit from zero') for p in bkgmodel.pars: p.val = 0 ui.fit_bkg(id=self.id) initial_v0 = self.calc_bkg_stat() # logf.info('fit: parameters: %s' % (final)) # logf.info('fit: stat: %s' % (initial_v0)) # pick the better starting point if initial_v0 < initial_v: # logf.info('fit: using zero-fit') initial_v = initial_v0 final = [p.val for p in ui.get_bkg_model(self.id).pars] else: # logf.info('fit: using decomposed-fit') for p, v in zip(bkgmodel.pars, final): p.val = v # start with the full fit and remove(freeze) parameters print('%d parameters, stat=%.2f' % (len(initial), initial_v)) results = [(2 * len(final) + initial_v, final, len(final), initial_v)] for i in range(len(initial) - 1, 0, -1): bkgmodel.pars[i].val = 0 bkgmodel.pars[i].freeze() ui.fit_bkg(id=self.id) final = [p.val for p in ui.get_bkg_model(self.id).pars] v = self.calc_bkg_stat() print('--> %d parameters, stat=%.2f' % (i, v)) results.insert(0, (v + 2 * i, final, i, v)) print() print('Background PCA fitting AIC results:') print('-----------------------------------') print() print('stat Ncomp AIC') for aic, params, nparams, val in results: print('%-05.1f %2d %-05.1f' % (val, nparams, aic)) aic, final, nparams, val = min(results) for p, v in zip(bkgmodel.pars, final): p.val = v for i in range(nparams): bkgmodel.pars[i].thaw() print() print('Increasing parameters again...') # now increase the number of parameters again # results = [(aic, final, nparams, val)] last_aic, last_final, last_nparams, last_val = aic, final, nparams, val for i in range(last_nparams, len(bkgmodel.pars)): next_nparams = i + 1 bkgmodel.pars[i].thaw() for p, v in zip(bkgmodel.pars, last_final): p.val = v ui.fit_bkg(id=self.id) next_final = [p.val for p in ui.get_bkg_model(self.id).pars] v = self.calc_bkg_stat() next_aic = v + 2 * next_nparams if next_aic < last_aic: # accept print('%d parameters, aic=%.2f ** accepting' % (next_nparams, next_aic)) last_aic, last_final, last_nparams, last_val = next_aic, next_final, next_nparams, v else: print('%d parameters, aic=%.2f' % (next_nparams, next_aic)) # stop if we are 3 parameters ahead what we needed if next_nparams >= last_nparams + 3: break print('Final choice: %d parameters, aic=%.2f' % (last_nparams, last_aic)) # reset to the last good solution for p, v in zip(bkgmodel.pars, last_final): p.val = v last_model = convbkgmodel for i in range(10): print('Adding Gaussian#%d' % (i + 1)) # find largest discrepancy ui.set_analysis(self.id, "ener", "rate") m = ui.get_bkg_fit_plot(self.id) y = m.dataplot.y.cumsum() z = m.modelplot.y.cumsum() diff_rate = np.abs(y - z) ui.set_analysis(self.id, "ener", "counts") m = ui.get_bkg_fit_plot(self.id) x = m.dataplot.x y = m.dataplot.y.cumsum() z = m.modelplot.y.cumsum() diff = np.abs(y - z) i = np.argmax(diff) energies = x e = x[i] print( 'largest remaining discrepancy at %.3fkeV[%d], need %d counts' % (x[i], i, diff[i])) # e = x[i] power = diff_rate[i] # lets try to inject a gaussian there g = ui.xsgaussian('g_%d_%d' % (self.id, i)) print('placing gaussian at %.2fkeV, with power %s' % (e, power)) # we work in energy bins, not energy g.LineE.min = energies[0] g.LineE.max = energies[-1] g.LineE.val = e if i > len(diff) - 2: i = len(diff) - 2 if i < 2: i = 2 g.Sigma = (x[i + 1] - x[i - 1]) g.Sigma.min = (x[i + 1] - x[i - 1]) / 3 g.Sigma.max = x[-1] - x[0] g.norm.min = power * 1e-6 g.norm.val = power convbkgmodel2 = response(g) next_model = last_model + convbkgmodel2 ui.set_bkg_full_model(self.id, next_model) ui.fit_bkg(id=self.id) next_final = [p.val for p in ui.get_bkg_model(self.id).pars] next_nparams = len(next_final) v = self.calc_bkg_stat() next_aic = v + 2 * next_nparams print('with Gaussian:', next_aic, '; change: %.1f (negative is good)' % (next_aic - last_aic)) if next_aic < last_aic: print('accepting') last_model = next_model last_aic, last_final, last_nparams, last_val = next_aic, next_final, next_nparams, v else: print('not significant, rejecting') ui.set_bkg_full_model(self.id, last_model) for p, v in zip(last_model.pars, last_final): p.val = v if v == 0: # the parameter was frozen. ui.freeze(p) break self.cstat, self.dof = self.calc_bkg_stat( dof=True) # Save the final cstat and dof (dof = ihi - ilo) self.filter_energy = ui.get_filter( ) # Save the filter for background fitting. ui.set_analysis('channel') ui.ignore() ui.notice(self.filter0) # restore filter ui.set_analysis('energy')
sh.set_stat("cash") sh.set_method("simplex") sh.load_image("G300-0_test_counts.fits") sh.set_coord("logical") sh.load_table_model("expo", "G300-0_test_exposure.fits") sh.load_table_model("bkg", "G300-0_test_background.fits") sh.load_psf("psf", "G300-0_test_psf.fits") # In principle one might first want to fit the background amplitude. However the background estimation method already yields the correct normalization, so we freeze the background amplitude to unity instead of adjusting it. The (smoothed) residuals from this background model are then computed and shown. # In[3]: sh.set_full_model(bkg) bkg.ampl = 1 sh.freeze(bkg) data = sh.get_data_image().y - sh.get_model_image().y resid = SkyImage(data=data, wcs=wcs) resid_table = [] # Keep residual images in a list to show them later resid_smo6 = resid.smooth(radius = 6) resid_smo6.plot() resid_table.append(resid_smo6) # ### Find and fit the brightest source # We then find the position of the maximum in the (smoothed) residuals map, and fit a (symmetrical) Gaussian source with that initial position: