def profile_norm(self, name, logemin=None, logemax=None, reoptimize=False,
                     xvals=None, npts=None, fix_shape=True, savestate=True,
                     **kwargs):
        """Profile the normalization of a source.

        Parameters
        ----------

        name : str
            Source name.

        reoptimize : bool
            Re-optimize free parameters in the model at each point
            in the profile likelihood scan.

        """

        self.logger.debug('Profiling %s', name)

        if savestate:
            saved_state = LikelihoodState(self.like)

        if fix_shape:
            self.free_sources(False, pars='shape', loglevel=logging.DEBUG)

        if npts is None:
            npts = self.config['gtlike']['llscan_npts']

        # Find the source
        name = self.roi.get_source_by_name(name).name
        parName = self.like.normPar(name).getName()

        loge_bounds = self.loge_bounds
        if logemin is not None or logemax is not None:
            self.set_energy_range(logemin, logemax)

        # Find a sequence of values for the normalization scan
        if xvals is None:
            if reoptimize:
                xvals = self._find_scan_pts_reopt(name, npts=npts,
                                                  **kwargs)
            else:
                xvals = self._find_scan_pts(name, npts=9)
                lnlp = self.profile(name, parName,
                                    reoptimize=False, xvals=xvals)
                lims = utils.get_parameter_limits(lnlp['xvals'],
                                                  lnlp['dloglike'],
                                                  cl_limit=0.99)

                if not np.isfinite(lims['ul']):
                    self.logger.warning('Upper limit not found.  '
                                        'Refitting normalization.')
                    self.like.optimize(0)
                    xvals = self._find_scan_pts(name, npts=npts)
                    lnlp = self.profile(name, parName,
                                        reoptimize=False,
                                        xvals=xvals)
                    lims = utils.get_parameter_limits(lnlp['xvals'],
                                                      lnlp['dloglike'],
                                                      cl_limit=0.99)

                if np.isfinite(lims['ll']):
                    xhi = np.linspace(lims['x0'], lims['ul'], npts - npts // 2)
                    xlo = np.linspace(lims['ll'], lims['x0'], npts // 2)
                    xvals = np.concatenate((xlo[:-1], xhi))
                    xvals = np.insert(xvals, 0, 0.0)
                elif np.abs(lnlp['dloglike'][0] - lims['lnlmax']) > 0.1:
                    lims['ll'] = 0.0
                    xhi = np.linspace(lims['x0'], lims['ul'],
                                      (npts + 1) - (npts + 1) // 2)
                    xlo = np.linspace(lims['ll'], lims['x0'], (npts + 1) // 2)
                    xvals = np.concatenate((xlo[:-1], xhi))
                else:
                    xvals = np.linspace(0, lims['ul'], npts)

        o = self.profile(name, parName,
                         reoptimize=reoptimize, xvals=xvals,
                         savestate=savestate, **kwargs)

        if savestate:
            saved_state.restore()

        if logemin is not None or logemax is not None:
            self.set_energy_range(*loge_bounds)

        self.logger.debug('Finished')

        return o