# Licensed under a 3-clause BSD style license - see LICENSE.rst
from __future__ import absolute_import, division, print_function
import os
import copy
import json
import pprint
import logging
import numpy as np
from astropy.io import fits
from astropy.table import Table, Column
from astropy.coordinates import SkyCoord
import fermipy.config
from fermipy import utils
from fermipy import defaults
from fermipy.config import ConfigSchema
from fermipy.gtutils import SourceMapState, FreeParameterState
from fermipy.timing import Timer
from fermipy.data_struct import MutableNamedTuple
from fermipy import fits_utils
from LikelihoodState import LikelihoodState
class ExtensionFit(object):
"""Mixin class which provides extension fitting to
`~fermipy.gtanalysis.GTAnalysis`."""
def extension(self, name, **kwargs):
"""Test this source for spatial extension with the likelihood
ratio method (TS_ext). This method will substitute an
extended spatial model for the given source and perform a
one-dimensional scan of the spatial extension parameter over
the range specified with the width parameters. The 1-D
profile likelihood is then used to compute the best-fit value,
upper limit, and TS for extension. The nuisance parameters
that will be simultaneously fit when performing the spatial
scan can be controlled with the ``fix_shape``,
``free_background``, and ``free_radius`` options. By default
the position of the source will be fixed to its current
position. A simultaneous fit to position and extension can be
performed by setting ``fit_position`` to True.
Parameters
----------
name : str
Source name.
{options}
optimizer : dict
Dictionary that overrides the default optimizer settings.
Returns
-------
extension : dict
Dictionary containing results of the extension analysis. The same
dictionary is also saved to the dictionary of this source under
'extension'.
"""
timer = Timer.create(start=True)
name = self.roi.get_source_by_name(name).name
schema = ConfigSchema(self.defaults['extension'],
optimizer=self.defaults['optimizer'])
schema.add_option('prefix', '')
schema.add_option('outfile', None, '', str)
config = utils.create_dict(self.config['extension'],
optimizer=self.config['optimizer'])
config = schema.create_config(config, **kwargs)
self.logger.info('Running extension fit for %s', name)
free_state = FreeParameterState(self)
ext = self._extension(name, **config)
free_state.restore()
self.logger.info('Finished extension fit.')
if config['make_plots']:
self._plotter.make_extension_plots(ext, self.roi,
prefix=config['prefix'])
outfile = config.get('outfile', None)
if outfile is None:
outfile = utils.format_filename(self.workdir, 'ext',
prefix=[config['prefix'],
name.lower().replace(' ', '_')])
else:
outfile = os.path.join(self.workdir,
os.path.splitext(outfile)[0])
if config['write_fits']:
self._make_extension_fits(ext, outfile + '.fits')
if config['write_npy']:
np.save(outfile + '.npy', dict(ext))
self.logger.info('Execution time: %.2f s', timer.elapsed_time)
return ext
def _extension(self, name, **kwargs):
spatial_model = kwargs['spatial_model']
width_min = kwargs['width_min']
width_max = kwargs['width_max']
width_nstep = kwargs['width_nstep']
width = kwargs['width']
free_background = kwargs['free_background']
free_radius = kwargs.get('free_radius', None)
fix_shape = kwargs.get('fix_shape', False)
make_tsmap = kwargs.get('make_tsmap', False)
tsmap_fitter = kwargs.get('tsmap_fitter', 'tsmap')
update = kwargs['update']
sqrt_ts_threshold = kwargs['sqrt_ts_threshold']
if kwargs['psf_scale_fn']:
def psf_scale_fn(t): return 1.0 + np.interp(np.log10(t),
kwargs['psf_scale_fn'][0],
kwargs['psf_scale_fn'][1])
else:
psf_scale_fn = None
saved_state = LikelihoodState(self.like)
loglike_init = -self.like()
self.logger.debug('Initial Model Log-Likelihood: %f', loglike_init)
if not free_background:
self.free_sources(free=False, loglevel=logging.DEBUG)
if free_radius is not None:
diff_sources = [s.name for s in self.roi.sources if s.diffuse]
skydir = self.roi[name].skydir
free_srcs = [s.name for s in
self.roi.get_sources(skydir=skydir,
distance=free_radius,
exclude=diff_sources)]
self.free_sources_by_name(free_srcs, pars='norm',
loglevel=logging.DEBUG)
# Fit baseline model
self.free_source(name, loglevel=logging.DEBUG)
if fix_shape:
self.free_source(name, free=False, pars='shape',
loglevel=logging.DEBUG)
fit_output = self._fit(loglevel=logging.DEBUG, **kwargs['optimizer'])
src = self.roi.copy_source(name)
# Save likelihood value for baseline fit
saved_state_base = LikelihoodState(self.like)
loglike_base = fit_output['loglike']
self.logger.debug('Baseline Model Log-Likelihood: %f', loglike_base)
if not width:
width = np.logspace(np.log10(width_min), np.log10(width_max),
width_nstep)
width = np.array(width)
width = width[width > 0]
width = np.concatenate(([0.0], np.array(width)))
o = defaults.make_default_tuple(defaults.extension_output)
o.name = name
o.width = width
o.dloglike = np.zeros(len(width) + 1)
o.loglike = np.zeros(len(width) + 1)
o.loglike_base = loglike_base
o.loglike_init = loglike_init
o.config = kwargs
o.ebin_ext = np.ones(self.enumbins) * np.nan
o.ebin_ext_err = np.ones(self.enumbins) * np.nan
o.ebin_ext_err_lo = np.ones(self.enumbins) * np.nan
o.ebin_ext_err_hi = np.ones(self.enumbins) * np.nan
o.ebin_ext_ul95 = np.ones(self.enumbins) * np.nan
o.ebin_ts_ext = np.ones(self.enumbins) * np.nan
o.ebin_loglike = np.ones((self.enumbins, len(width))) * np.nan
o.ebin_dloglike = np.ones((self.enumbins, len(width))) * np.nan
o.ebin_loglike_ptsrc = np.ones(self.enumbins) * np.nan
o.ebin_loglike_ext = np.ones(self.enumbins) * np.nan
o.ebin_e_min = self.energies[:-1]
o.ebin_e_max = self.energies[1:]
o.ebin_e_ctr = np.sqrt(o.ebin_e_min * o.ebin_e_max)
self.logger.debug('Width scan vector:\n %s', width)
if kwargs['fit_position']:
ext_fit = self._fit_extension_full(name,
spatial_model=spatial_model,
optimizer=kwargs['optimizer'])
else:
ext_fit = self._fit_extension(name,
spatial_model=spatial_model,
optimizer=kwargs['optimizer'],
psf_scale_fn=psf_scale_fn)
o.update(ext_fit)
# Fit with the best-fit extension model
self.logger.info('Fitting extended-source model.')
self.set_source_morphology(name, spatial_model=spatial_model,
spatial_pars={'ra': o['ra'], 'dec': o['dec'],
'SpatialWidth': o['ext']},
use_pylike=False,
psf_scale_fn=psf_scale_fn)
# Perform scan over width parameter
o.loglike = self._scan_extension(name,
spatial_model=spatial_model,
width=width,
optimizer=kwargs['optimizer'],
psf_scale_fn=psf_scale_fn)
self.set_source_morphology(name, spatial_model=spatial_model,
spatial_pars={'ra': o['ra'], 'dec': o['dec'],
'SpatialWidth': o['ext']},
use_pylike=False,
psf_scale_fn=psf_scale_fn)
fit_output = self._fit(loglevel=logging.DEBUG, update=False,
**kwargs['optimizer'])
o.source_fit = self.get_src_model(name, reoptimize=True,
optimizer=kwargs['optimizer'])
o.loglike_ext = fit_output['loglike']
if kwargs['fit_ebin']:
self._fit_extension_ebin(name, o, **kwargs)
if kwargs['save_model_map']:
o.ext_tot_map = self.model_counts_map()
o.ext_src_map = self.model_counts_map(name)
o.ext_bkg_map = self.model_counts_map(exclude=[name])
if make_tsmap:
tsmap_model = {'SpatialModel': 'RadialDisk',
'SpatialWidth': 0.1 * 0.8246211251235321}
tsmap_model.update(src.spectral_pars)
self.logger.info('Generating TS map.')
if tsmap_fitter == 'tsmap':
tsmap = self.tsmap(model=tsmap_model,
map_skydir=SkyCoord(o['ra'], o['dec'], unit='deg'),
map_size=max(1.0, 4.0 * o['ext']),
exclude=[name],
write_fits=False,
write_npy=False,
use_pylike=False,
make_plots=False,
loglevel=logging.DEBUG)
o.tsmap = tsmap['ts']
elif tsmap_fitter == 'tscube':
tscube = self.tscube(model=tsmap_model,
map_skydir=SkyCoord(o['ra'], o['dec'], unit='deg'),
map_size=max(1.0, 4.0 * o['ext']),
do_sed=False,
write_fits=False,
write_npy=False,
make_plots=False,
loglevel=logging.DEBUG)
o.tsmap = tscube['ts']
self.logger.info('Testing point-source model.')
# Test point-source hypothesis
self.set_source_morphology(name, spatial_model='PointSource',
use_pylike=False,
psf_scale_fn=psf_scale_fn)
# Fit a point-source
saved_state_base.restore()
self.logger.debug('Fitting point-source model.')
fit_output = self._fit(loglevel=logging.DEBUG, **kwargs['optimizer'])
if src['SpatialModel'] == 'PointSource' and kwargs['fit_position']:
loc = self.localize(name, update=False)
o.loglike_ptsrc = loc['loglike_loc']
else:
o.loglike_ptsrc = fit_output['loglike']
o.dloglike = o.loglike - o.loglike_ptsrc
o.ts_ext = 2 * (o.loglike_ext - o.loglike_ptsrc)
self.logger.debug('Point-Source Model Likelihood: %f', o.loglike_ptsrc)
if kwargs['save_model_map']:
o.ptsrc_tot_map = self.model_counts_map()
o.ptsrc_src_map = self.model_counts_map(name)
o.ptsrc_bkg_map = self.model_counts_map(exclude=[name])
if update and (sqrt_ts_threshold is None or
np.sqrt(o['ts_ext']) > sqrt_ts_threshold):
src = self.delete_source(name, loglevel=logging.DEBUG)
src.set_spatial_model(spatial_model,
{'ra': o.ra, 'dec': o.dec,
'SpatialWidth': o.ext})
# FIXME: Issue with source map cache with source is
# initialized as fixed.
self.add_source(name, src, free=True, loglevel=logging.DEBUG)
self.free_source(name, loglevel=logging.DEBUG)
if fix_shape:
self.free_source(name, free=False, pars='shape',
loglevel=logging.DEBUG)
fit_output = self.fit(loglevel=logging.DEBUG,
**kwargs['optimizer'])
o.loglike_ext = fit_output['loglike']
src = self.roi.get_source_by_name(name)
if kwargs['fit_position']:
for k in ['ra_err', 'dec_err', 'glon_err', 'glat_err',
'pos_err', 'pos_err_semimajor', 'pos_err_semiminor',
'pos_r68', 'pos_r95', 'pos_r99', 'pos_angle']:
src[k] = o[k]
else:
self.set_source_morphology(name, spatial_model=src['SpatialModel'],
spatial_pars=src.spatial_pars,
update_source=False)
# Restore ROI to previous state
saved_state.restore()
self._sync_params(name)
self._update_roi()
self.logger.info('Best-fit extension: %6.4f + %6.4f - %6.4f'
% (o['ext'], o['ext_err_hi'], o['ext_err_lo']))
self.logger.info('TS_ext: %.3f' % o['ts_ext'])
self.logger.info('Extension UL: %6.4f' % o['ext_ul95'])
self.logger.info('LogLike: %12.3f DeltaLogLike: %12.3f',
o.loglike_ext, o.loglike_ext - o.loglike_init)
if kwargs['fit_position']:
self.logger.info('Position:\n'
'( ra, dec) = (%10.4f +/- %8.4f,%10.4f +/- %8.4f)\n'
'(glon,glat) = (%10.4f +/- %8.4f,%10.4f +/- %8.4f)\n'
'offset = %8.4f r68 = %8.4f r95 = %8.4f r99 = %8.4f',
o.ra, o.ra_err, o.dec, o.dec_err,
o.glon, o.glon_err, o.glat, o.glat_err,
o.pos_offset, o.pos_r68, o.pos_r95, o.pos_r99)
return o
def _make_extension_fits(self, ext, filename, **kwargs):
maps = {'EXT_TOT_MAP': ext['ext_tot_map'],
'EXT_SRC_MAP': ext['ext_src_map'],
'EXT_BKG_MAP': ext['ext_bkg_map'],
'PTSRC_TOT_MAP': ext['ptsrc_tot_map'],
'PTSRC_SRC_MAP': ext['ptsrc_src_map'],
'PTSRC_BKG_MAP': ext['ptsrc_bkg_map']}
hdu_images = []
for k, v in sorted(maps.items()):
if v is None:
continue
hdu_images += [v.make_hdu(k)]
tab = fits_utils.dict_to_table(ext)
hdu_data = fits.table_to_hdu(tab)
hdu_data.name = 'EXT_DATA'
if ext.get('tsmap'):
hdus = [ext['tsmap'].make_hdu(hdu='PRIMARY')]
else:
hdus = [fits.PrimaryHDU()]
hdus += [hdu_data] + hdu_images
hdus[0].header['CONFIG'] = json.dumps(utils.tolist(ext['config']))
hdus[1].header['CONFIG'] = json.dumps(utils.tolist(ext['config']))
fits_utils.write_hdus(hdus, filename,
keywords={'SRCNAME': ext['name']})
def _fit_extension_ebin(self, name, o, **kwargs):
optimizer = kwargs.get('optimizer', {})
spatial_model = kwargs.get('spatial_model')
psf_scale_fn = kwargs.pop('psf_scale_fn', None)
reoptimize = kwargs.pop('reoptimize', True)
src = self.roi.copy_source(name)
self.set_source_morphology(name, spatial_model='PointSource',
use_pylike=False,
psf_scale_fn=psf_scale_fn)
for i, (logemin, logemax) in enumerate(zip(self.log_energies[:-1],
self.log_energies[1:])):
self.set_energy_range(logemin, logemax)
o.ebin_loglike_ptsrc[i] = -self.like()
self.set_energy_range(self.log_energies[0], self.log_energies[-1])
self.set_source_morphology(name, spatial_model=src['SpatialModel'],
spatial_pars=src.spatial_pars,
psf_scale_fn=psf_scale_fn,
use_pylike=False)
o.ebin_loglike = self._scan_extension_fast_ebin(name,
spatial_model=spatial_model,
width=o.width,
optimizer=kwargs[
'optimizer'],
psf_scale_fn=psf_scale_fn,
reoptimize=False)
for i, (logemin, logemax) in enumerate(zip(self.log_energies[:-1],
self.log_energies[1:])):
ul_data = utils.get_parameter_limits(o.width, o.ebin_loglike[i])
o.ebin_ext[i] = max(ul_data['x0'], 10**-2.5)
o.ebin_ext_err[i] = ul_data['err']
o.ebin_ext_err_lo[i] = ul_data['err_lo']
o.ebin_ext_err_hi[i] = ul_data['err_hi']
o.ebin_ext_ul95[i] = ul_data['ul']
o.ebin_loglike_ext[i] = ul_data['lnlmax']
o.ebin_ts_ext[i] = 2.0 * \
(o.ebin_loglike_ext[i] - o.ebin_loglike_ptsrc[i])
o.ebin_dloglike = o.ebin_loglike - o.ebin_loglike_ptsrc[:, None]
self.set_source_morphology(name, spatial_model=src['SpatialModel'],
spatial_pars=src.spatial_pars,
use_pylike=False)
def _scan_extension(self, name, **kwargs):
saved_state = LikelihoodState(self.like)
if not hasattr(self.components[0].like.logLike, 'setSourceMapImage'):
loglike = self._scan_extension_pylike(name, **kwargs)
else:
loglike = self._scan_extension_fast(name, **kwargs)
saved_state.restore()
return loglike
def _scan_extension_fast(self, name, **kwargs):
state = SourceMapState(self.like, [name])
self.free_norm(name)
optimizer = kwargs.get('optimizer', {})
width = kwargs.get('width')
spatial_model = kwargs.get('spatial_model')
skydir = kwargs.pop('skydir', self.roi[name].skydir)
psf_scale_fn = kwargs.pop('psf_scale_fn', None)
reoptimize = kwargs.pop('reoptimize', True)
src = self.roi.copy_source(name)
spatial_pars = {'ra': skydir.ra.deg, 'dec': skydir.dec.deg}
loglike = []
for i, w in enumerate(width):
spatial_pars['SpatialWidth'] = max(w, 0.00316)
self.set_source_morphology(name,
spatial_model=spatial_model,
spatial_pars=spatial_pars,
use_pylike=False,
psf_scale_fn=psf_scale_fn)
if reoptimize:
fit_output = self._fit(loglevel=logging.DEBUG, **optimizer)
loglike += [fit_output['loglike']]
else:
loglike += [-self.like()]
state.restore()
return np.array(loglike)
def _scan_extension_fast_ebin(self, name, **kwargs):
state = SourceMapState(self.like, [name])
self.free_norm(name)
optimizer = kwargs.get('optimizer', {})
width = kwargs.get('width')
spatial_model = kwargs.get('spatial_model')
skydir = kwargs.pop('skydir', self.roi[name].skydir)
psf_scale_fn = kwargs.pop('psf_scale_fn', None)
reoptimize = kwargs.pop('reoptimize', True)
src = self.roi.copy_source(name)
spatial_pars = {'ra': skydir.ra.deg, 'dec': skydir.dec.deg}
loglike = np.ones((self.enumbins, len(width)))
for i, w in enumerate(width):
spatial_pars['SpatialWidth'] = max(w, 0.00316)
self.set_source_morphology(name,
spatial_model=spatial_model,
spatial_pars=spatial_pars,
use_pylike=False,
psf_scale_fn=psf_scale_fn)
for j, (logemin, logemax) in enumerate(zip(self.log_energies[:-1],
self.log_energies[1:])):
self.set_energy_range(logemin, logemax)
if reoptimize:
fit_output = self._fit(loglevel=logging.DEBUG, **optimizer)
loglike[j, i] = fit_output['loglike']
else:
loglike[j, i] = -self.like()
self.set_energy_range(self.log_energies[0], self.log_energies[-1])
state.restore()
return loglike
def _scan_extension_pylike(self, name, **kwargs):
optimizer = kwargs.get('optimizer', {})
width = kwargs.get('width')
spatial_model = kwargs.get('spatial_model')
skydir = kwargs.pop('skydir', self.roi[name].skydir)
src = self.roi.copy_source(name)
self._fitcache = None
spatial_pars = {'ra': skydir.ra.deg, 'dec': skydir.dec.deg}
loglike = []
for i, w in enumerate(width):
spatial_pars['SpatialWidth'] = max(w, 0.00316)
self.set_source_morphology(name,
spatial_model=spatial_model,
spatial_pars=spatial_pars,
use_pylike=False)
fit_output = self._fit(loglevel=logging.DEBUG, **optimizer)
loglike += [fit_output['loglike']]
self.set_source_morphology(name, spatial_model=src['SpatialModel'],
spatial_pars=src.spatial_pars)
return np.array(loglike)
def _fit_extension(self, name, **kwargs):
spatial_model = kwargs.get('spatial_model', 'RadialGaussian')
optimizer = kwargs.get('optimizer', {})
fit_position = kwargs.get('fit_position', False)
skydir = kwargs.get('skydir', self.roi[name].skydir)
psf_scale_fn = kwargs.get('psf_scale_fn', None)
reoptimize = kwargs.get('reoptimize', True)
src = self.roi.copy_source(name)
# If the source is extended split the likelihood scan into two
# parts centered on the best-fit value -- this ensures better
# fit stability
if (src['SpatialModel'] in ['RadialGaussian', 'RadialDisk'] and
src['SpatialWidth'] > 0.1):
width_lo = np.logspace(-2.0, np.log10(src['SpatialWidth']), 11)
width_hi = np.logspace(np.log10(src['SpatialWidth']), 0.5, 11)
loglike_lo = self._scan_extension(name, spatial_model=spatial_model,
width=width_lo[::-1],
optimizer=optimizer,
skydir=skydir,
psf_scale_fn=psf_scale_fn,
reoptimize=reoptimize)[::-1]
loglike_hi = self._scan_extension(name, spatial_model=spatial_model,
width=width_hi,
optimizer=optimizer,
skydir=skydir,
psf_scale_fn=psf_scale_fn,
reoptimize=reoptimize)
width = np.concatenate((width_lo, width_hi[1:]))
loglike = np.concatenate((loglike_lo, loglike_hi[1:]))
else:
width = np.logspace(-2.0, 0.5, 21)
width = np.concatenate(([0.0], width))
loglike = self._scan_extension(name, spatial_model=spatial_model,
width=width, optimizer=optimizer,
skydir=skydir,
psf_scale_fn=psf_scale_fn,
reoptimize=reoptimize)
ul_data = utils.get_parameter_limits(width, loglike,
bounds=[10**-3.0, 10**0.5])
if not np.isfinite(ul_data['err']):
ul_data['x0'] = width[np.argmax(loglike)]
ul_data['err'] = ul_data['x0']
ul_data['err_lo'] = ul_data['x0']
ul_data['err_hi'] = ul_data['x0']
imax = np.argmax(loglike)
err = max(10**-2.0, ul_data['err'])
lolim = max(min(ul_data['x0'], width[imax]) - 2.0 * err, 0)
if np.isfinite(ul_data['ul']):
hilim = 1.5 * ul_data['ul']
else:
hilim = ul_data['x0'] + 2.0 * err
nstep = max(11, int((hilim - lolim) / err))
width2 = np.linspace(lolim, hilim, nstep)
loglike2 = self._scan_extension(name, spatial_model=spatial_model,
width=width2, optimizer=optimizer,
skydir=skydir,
psf_scale_fn=psf_scale_fn,
reoptimize=reoptimize)
ul_data2 = utils.get_parameter_limits(width2, loglike2,
bounds=[10**-3.0, 10**0.5])
self.logger.debug('width: %s', width)
self.logger.debug('loglike: %s', loglike - np.max(loglike))
self.logger.debug('ul_data:\n %s', pprint.pformat(ul_data))
self.logger.debug('width2: %s', width2)
self.logger.debug('loglike2: %s', loglike2 - np.max(loglike2))
self.logger.debug('ul_data2:\n %s', pprint.pformat(ul_data2))
return MutableNamedTuple(
ext=max(ul_data2['x0'], 10**-2.5),
ext_ul95=ul_data2['ul'],
ext_err_lo=ul_data2['err_lo'],
ext_err_hi=ul_data2['err_hi'],
ext_err=ul_data2['err'],
loglike_ext=ul_data2['lnlmax'],
ra=skydir.ra.deg,
dec=skydir.dec.deg,
glon=skydir.galactic.l.deg,
glat=skydir.galactic.b.deg,
pos_offset=0.0)
def _fit_extension_full(self, name, **kwargs):
skydir = self.roi[name].skydir.copy()
src = self.roi.copy_source(name)
spatial_model = kwargs.get('spatial_model', 'RadialGaussian')
loglike = -self.like()
o = MutableNamedTuple(
ext=np.nan, ext_ul95=np.nan,
ext_err_lo=np.nan, ext_err_hi=np.nan,
ext_err=np.nan,
loglike_ext=np.nan,
ra=np.nan, dec=np.nan, glon=np.nan, glat=np.nan,
ra_err=np.nan, dec_err=np.nan,
glon_err=np.nan, glat_err=np.nan,
pos_err=np.nan, pos_r68=np.nan,
pos_r95=np.nan, pos_r99=np.nan,
pos_err_semimajor=np.nan, pos_err_semiminor=np.nan,
pos_angle=np.nan, pos_offset=np.nan)
t0 = Timer()
t1 = Timer()
nstep = 7
for i in range(4):
t0.start()
fit_ext = self._fit_extension(name, skydir=skydir, **kwargs)
o.update(fit_ext)
self.set_source_morphology(name,
spatial_model=spatial_model,
spatial_pars={
'SpatialWidth': max(o.ext, 0.00316)},
use_pylike=False)
t0.stop()
t1.start()
if i == 0 or not np.isfinite(o.pos_r99):
dtheta_max = max(0.5, 1.5 * o.ext)
else:
dtheta_max = max(0.5, o.pos_r99)
fit_pos0, fit_pos1 = self._fit_position(name, nstep=nstep,
dtheta_max=dtheta_max,
zmin=-3.0, use_pylike=False)
o.update(fit_pos0)
t1.stop()
self.set_source_morphology(name,
spatial_model=spatial_model,
spatial_pars={'RA': o['ra'],
'DEC': o['dec']},
use_pylike=False)
self.logger.debug('Elapsed Time: %.2f %.2f',
t0.elapsed_time, t1.elapsed_time)
fit_output = self._fit(
loglevel=logging.DEBUG, **kwargs['optimizer'])
skydir = fit_pos0['skydir']
o.pos_offset = skydir.separation(src.skydir).deg
o.loglike_ext = -self.like()
dloglike = o.loglike_ext - loglike
self.logger.info('Iter %i R68 = %8.3f Offset = %8.3f '
'LogLikelihood = %10.2f Delta-LogLikelihood = %8.2f',
i, o.ext, o.pos_offset, o.loglike_ext,
dloglike)
if i > 0 and dloglike < 0.1:
break
loglike = o.loglike_ext
self.set_source_morphology(name, spatial_pars=src.spatial_pars,
use_pylike=False)
return o