Beispiel #1
0
    def __call__(self, params):
        def _getpar(key):
            return synth_params['{}{}'.format(key, self.planet_num)].value

        synth_params = params.basis.to_synth(params)

        tp = _getpar('tp')
        per = _getpar('per')
        ecc = _getpar('e')
        omega = _getpar('w')

        ts = orbit.timeperi_to_timetrans(tp, per, ecc, omega, secondary=True)
        ts_phase = utils.t_to_phase(synth_params, ts, self.planet_num)

        pts = utils.t_to_phase(synth_params, self.ts, self.planet_num)
        epts = self.ts_err / per

        penalty = -0.5 * ((ts_phase - pts) / epts)**2

        return penalty
Beispiel #2
0
    def plot_phasefold(self, pltletter, pnum):
        """
        Plot phased orbit plots for each planet in the fit.

        Args:
            pltletter (int): integer representation of 
                letter to be printed in the corner of the first
                phase plot.
                Ex: ord("a") gives 97, so the input should be 97.
            pnum (int): the number of the planet to be plotted. Must be
                the same as the number used to define a planet's 
                Parameter objects (e.g. 'per1' is for planet #1)

        """

        ax = pl.gca()

        if len(self.post.likelihood.x) < 20:
            self.nobin = True

        bin_fac = 1.75
        bin_markersize = bin_fac * rcParams['lines.markersize']
        bin_markeredgewidth = bin_fac * rcParams['lines.markeredgewidth']

        rvmod2 = self.model(self.rvmodt, planet_num=pnum) - self.slope
        modph = t_to_phase(self.post.params, self.rvmodt, pnum, cat=True) - 1
        rvdat = self.rawresid + self.model(self.rvtimes,
                                           planet_num=pnum) - self.slope_low
        phase = t_to_phase(self.post.params, self.rvtimes, pnum, cat=True) - 1
        rvdatcat = np.concatenate((rvdat, rvdat))
        rverrcat = np.concatenate((self.rverr, self.rverr))
        rvmod2cat = np.concatenate((rvmod2, rvmod2))
        bint, bindat, binerr = fastbin(phase + 1, rvdatcat, nbins=25)
        bint -= 1.0

        ax.axhline(
            0,
            color='0.5',
            linestyle='--',
        )
        ax.plot(sorted(modph),
                rvmod2cat[np.argsort(modph)],
                'b-',
                linewidth=self.fit_linewidth)
        plot.labelfig(pltletter)

        telcat = np.concatenate(
            (self.post.likelihood.telvec, self.post.likelihood.telvec))

        if self.highlight_last:
            ind = np.argmax(self.rvtimes)
            hphase = t_to_phase(self.post.params,
                                self.rvtimes[ind],
                                pnum,
                                cat=False)
            if hphase > 0.5:
                hphase -= 1
            pl.plot(hphase, rvdatcat[ind], **plot.highlight_format)

        plot.mtelplot(phase,
                      rvdatcat,
                      rverrcat,
                      telcat,
                      ax,
                      telfmts=self.telfmts)
        if not self.nobin and len(rvdat) > 10:
            ax.errorbar(bint,
                        bindat,
                        yerr=binerr,
                        fmt='ro',
                        mec='w',
                        ms=bin_markersize,
                        mew=bin_markeredgewidth)

        if self.phase_limits:
            ax.set_xlim(self.phase_limits[0], self.phase_limits[1])
        else:
            ax.set_xlim(-0.5, 0.5)

        if not self.yscale_auto:
            scale = np.std(rvdatcat)
            ax.set_ylim(-self.yscale_sigma * scale, self.yscale_sigma * scale)

        keys = [p + str(pnum) for p in ['per', 'k', 'e']]

        labels = [self.post.params.tex_labels().get(k, k) for k in keys]
        if pnum < self.num_planets:
            ticks = ax.yaxis.get_majorticklocs()
            ax.yaxis.set_ticks(ticks[1:-1])

        ax.set_ylabel('RV [{ms:}]'.format(**plot.latex), weight='bold')
        ax.set_xlabel('Phase', weight='bold')

        print_params = ['per', 'k', 'e']
        units = {'per': 'days', 'k': plot.latex['ms'], 'e': ''}

        anotext = []
        for l, p in enumerate(print_params):
            val = self.post.params["%s%d" % (print_params[l], pnum)].value

            if self.uparams is None:
                _anotext = r'$\mathregular{%s}$ = %4.2f %s' % (
                    labels[l].replace("$", ""), val, units[p])
            else:
                if hasattr(self.post, 'medparams'):
                    val = self.post.medparams["%s%d" % (print_params[l], pnum)]
                else:
                    print("WARNING: medparams attribute not found in " +
                          "posterior object will annotate with " +
                          "max-likelihood values and reported uncertainties " +
                          "may not be appropriate.")
                err = self.uparams["%s%d" % (print_params[l], pnum)]
                if err > 1e-15:
                    val, err, errlow = sigfig(val, err)
                    _anotext = r'$\mathregular{%s}$ = %s $\mathregular{\pm}$ %s %s' \
                               % (labels[l].replace("$", ""), val, err, units[p])
                else:
                    _anotext = r'$\mathregular{%s}$ = %4.2f %s' % (
                        labels[l].replace("$", ""), val, units[p])

            anotext += [_anotext]

        if hasattr(self.post, 'derived'):
            chains = pd.read_csv(self.status['derive']['chainfile'])
            self.post.nplanets = self.num_planets
            dp = mcmc_plots.DerivedPlot(chains, self.post)
            labels = dp.labels
            texlabels = dp.texlabels
            units = dp.units
            derived_params = ['mpsini']
            for l, par in enumerate(derived_params):
                par_label = par + str(pnum)
                if par_label in self.post.derived.columns:
                    index = np.where(np.array(labels) == par_label)[0][0]

                    unit = units[index]
                    if unit == "M$_{\\rm Jup}$":
                        conversion_fac = 0.00315
                    elif unit == "M$_{\\odot}$":
                        conversion_fac = 0.000954265748
                    else:
                        conversion_fac = 1

                    val = self.post.derived["%s%d" %
                                            (derived_params[l],
                                             pnum)].loc[0.500] * conversion_fac
                    low = self.post.derived["%s%d" %
                                            (derived_params[l],
                                             pnum)].loc[0.159] * conversion_fac
                    high = self.post.derived[
                        "%s%d" %
                        (derived_params[l], pnum)].loc[0.841] * conversion_fac
                    err_low = val - low
                    err_high = high - val
                    err = np.mean([err_low, err_high])
                    err = radvel.utils.round_sig(err)
                    if err > 1e-15:
                        val, err, errlow = sigfig(val, err)
                        _anotext = r'$\mathregular{%s}$ = %s $\mathregular{\pm}$ %s %s' \
                                   % (texlabels[index].replace("$", ""), val, err, units[index])
                    else:
                        _anotext = r'$\mathregular{%s}$ = %4.2f %s' % (
                            texlabels[index].replace("$",
                                                     ""), val, units[index])

                    anotext += [_anotext]

        anotext = '\n'.join(anotext)
        plot.add_anchored(anotext,
                          loc=1,
                          frameon=True,
                          prop=dict(size=self.phasetext_size, weight='bold'),
                          bbox=dict(ec='none', fc='w', alpha=0.8))
Beispiel #3
0
    def plot_phasefold(self, pltletter, pnum):
        """
        Plot phased orbit plots for each planet in the fit.

        Args:
            pltletter (int): integer representation of
                letter to be printed in the corner of the first
                phase plot.
                Ex: ord("a") gives 97, so the input should be 97.
            pnum (int): the number of the planet to be plotted. Must be
                the same as the number used to define a planet's
                Parameter objects (e.g. 'per1' is for planet #1)

        """

        ax = plt.gca()

        if len(self.post.likelihood.x) < 20:
            self.nobin = True

        bin_fac = 1.75
        bin_markersize = bin_fac * rcParams['lines.markersize']
        bin_markeredgewidth = bin_fac * rcParams['lines.markeredgewidth']

        rvmod2 = self.model(self.rvmodt, planet_num=pnum) - self.slope
        modph = t_to_phase(self.post.params, self.rvmodt, pnum, cat=True) - 1
        rvdat = self.rawresid + self.model(self.rvtimes,
                                           planet_num=pnum) - self.slope_low
        phase = t_to_phase(self.post.params, self.rvtimes, pnum, cat=True) - 1
        rvdatcat = np.concatenate((rvdat, rvdat))
        rverrcat = np.concatenate((self.rverr, self.rverr))
        rvmod2cat = np.concatenate((rvmod2, rvmod2))
        bint, bindat, binerr = fastbin(phase + 1, rvdatcat, nbins=25)
        bint -= 1.0

        ax.axhline(0, color='k', linestyle=':', linewidth=1)
        ax.plot(sorted(modph),
                rvmod2cat[np.argsort(modph)],
                'k-',
                linewidth=self.fit_linewidth)
        #plot.labelfig(pltletter)

        telcat = np.concatenate(
            (self.post.likelihood.telvec, self.post.likelihood.telvec))

        if self.highlight_last:
            ind = np.argmax(self.rvtimes)
            hphase = t_to_phase(self.post.params,
                                self.rvtimes[ind],
                                pnum,
                                cat=False)
            if hphase > 0.5:
                hphase -= 1
            plt.plot(hphase, rvdatcat[ind], **plot.highlight_format)

        mtelplot(phase, rvdatcat, rverrcat, telcat, ax, telfmts=self.telfmts)
        if not self.nobin and len(rvdat) > 10:
            pass
            #ax.errorbar(
            #    bint, bindat, yerr=binerr, fmt='ro', mec='w', ms=bin_markersize,
            #    mew=bin_markeredgewidth
            #)

        if self.phase_limits:
            ax.set_xlim(self.phase_limits[0], self.phase_limits[1])
        else:
            ax.set_xlim(-0.5, 0.5)

        if not self.yscale_auto:
            scale = np.std(rvdatcat)
            ax.set_ylim(-self.yscale_sigma * scale, self.yscale_sigma * scale)
        ax.set_ylim((-510, 510))

        keys = [p + str(pnum) for p in ['per', 'k', 'e']]

        labels = [self.post.params.tex_labels().get(k, k) for k in keys]
        if pnum < self.num_planets:
            ticks = ax.yaxis.get_majorticklocs()
            ax.yaxis.set_ticks(ticks[1:-1])

        ax.set_ylabel('RV [{ms:}]'.format(**plot.latex))
        ax.set_xlabel('Phase')

        ax.get_yaxis().set_tick_params(which='both', direction='in')
        ax.get_xaxis().set_tick_params(which='both', direction='in')
        ax.tick_params(right=True, which='both', direction='in')
        ax.tick_params(top=True, which='both', direction='in')

        print_params = ['per', 'k', 'e']
        units = {'per': 'days', 'k': plot.latex['ms'], 'e': ''}

        anotext = []
        for l, p in enumerate(print_params):
            val = self.post.params["%s%d" % (print_params[l], pnum)].value

            if self.uparams is None:
                _anotext = '$\\mathregular{%s}$ = %4.2f %s' % (
                    labels[l].replace("$", ""), val, units[p])
            else:
                if hasattr(self.post, 'medparams'):
                    val = self.post.medparams["%s%d" % (print_params[l], pnum)]
                else:
                    print("WARNING: medparams attribute not found in " +
                          "posterior object will annotate with " +
                          "max-likelihood values and reported uncertainties " +
                          "may not be appropriate.")
                err = self.uparams["%s%d" % (print_params[l], pnum)]
                if err > 1e-15:
                    val, err, errlow = sigfig(val, err)
                    _anotext = '$\\mathregular{%s}$ = %s $\\mathregular{\\pm}$ %s %s' \
                               % (labels[l].replace("$", ""), val, err, units[p])
                else:
                    _anotext = '$\\mathregular{%s}$ = %4.2f %s' % (
                        labels[l].replace("$", ""), val, units[p])

            anotext += [_anotext]

        #anotext = '\n'.join(anotext)
        anotext = anotext[1]  # just the semi-amplitude
        logk1_limit = 4.68726682
        #anotext = (
        #    f'K < {np.exp(logk1_limit):.1f}' +' m$\,$s$^{-1}$ (3$\sigma$)\n'
        #    '$M_{\mathrm{p}} \sin i < 1.20\,M_{\mathrm{Jup}}$'
        #)
        anotext = (
            #f'K < {np.exp(logk1_limit):.1f}' +' m$\,$s$^{-1}$ (3$\sigma$)\n'
            '$M_{\mathrm{p}} \sin i < 1.20\,M_{\mathrm{Jup}}\ (3\sigma)$')

        add_anchored(anotext,
                     loc='lower left',
                     frameon=True,
                     prop=dict(size=self.phasetext_size),
                     bbox=dict(ec='none', fc='w', alpha=0.8))
Beispiel #4
0
def rv_multipanel_plot(post, saveplot=None, **kwargs):
    """
    Multi-panel RV plot to display model using post.params orbital paramters.

    Args:
        post (radvel.Posterior): Radvel posterior object. The model plotted will be generated from post.params
        saveplot (string): (optional) Name of output file, will show as interactive matplotlib window if not defined.
        nobin (bool): (optional) If True do not show binned data on phase plots. Will default to True if total number of measurements is less then 20.
        yscale_auto (bool): (optional) Use matplotlib auto y-axis scaling (default: False)
        yscale_sigma (float): (optional) Scale y-axis limits to be +/- yscale_sigma*(RMS of data plotted) (default: 3.0)   
        telfmts (dict): (optional) dictionary mapping instrument code to plotting format code
        nophase (bool): (optional) Will omit phase-folded plots if true
        epoch (float): (optional) Subtract this value from the time axis for more compact axis labels (default: 245000)
        uparams (dict): (optional) parameter uncertainties, must contain 'per', 'k', and 'e' keys  (default: None) 
    Returns:
        None
        
    """

    nobin = kwargs.pop('nobin', False)
    yscale_sigma = kwargs.pop('yscale_sigma', 3.0)
    yscale_auto = kwargs.pop('yscale_auto', False)
    telfmts = kwargs.pop('telfmts', globals()['telfmts'])
    nophase = kwargs.pop('nophase', False)
    e = kwargs.pop('epoch', 2450000)
    uparams = kwargs.pop('uparams', None)

    if len(post.likelihood.x) < 20: nobin = True

    if saveplot != None: resolution = 1e4
    else: resolution = 2000

    cpspost = copy.deepcopy(post)
    cpsparams = post.params.basis.to_cps(post.params)
    cpspost.params.update(cpsparams)

    model = cpspost.likelihood.model
    rvtimes = cpspost.likelihood.x
    rvdat = cpspost.likelihood.y
    rverr = cpspost.likelihood.errorbars()
    n = model.num_planets

    if isinstance(cpspost.likelihood, radvel.likelihood.CompositeLikelihood):
        like_list = cpspost.likelihood.like_list
    else:
        like_list = [cpspost.likelihood]

    periods = []
    for i in range(model.num_planets):
        periods.append(cpsparams['per%d' % (i + 1)])
    longp = max(periods)
    shortp = min(periods)

    dt = max(rvtimes) - min(rvtimes)
    rvmodt = np.linspace(
        min(rvtimes) - 0.05 * dt,
        max(rvtimes) + 0.05 * dt + longp, resolution)

    rvmod2 = model(rvmodt)
    rvmod = model(rvtimes)

    if ((rvtimes - e) < -2.4e6).any():
        plttimes = rvtimes
        mplttimes = rvmodt
    elif e == 0:
        e = 2450000
        plttimes = rvtimes - e
        mplttimes = rvmodt - e
    else:
        plttimes = rvtimes - e
        mplttimes = rvmodt - e

    rawresid = cpspost.likelihood.residuals()
    resid = rawresid + cpsparams['dvdt'] * (
        rvtimes - model.time_base) + cpsparams['curv'] * (rvtimes -
                                                          model.time_base)**2
    slope = cpsparams['dvdt'] * (
        rvmodt - model.time_base) + cpsparams['curv'] * (rvmodt -
                                                         model.time_base)**2
    slope_low = cpsparams['dvdt'] * (
        rvtimes - model.time_base) + cpsparams['curv'] * (rvtimes -
                                                          model.time_base)**2

    if nophase:
        fig = pl.figure(figsize=(19.0, 16.0))
        n = 0
        rect = [0.10, 0.12, 0.865, 1 - 0.12 - 0.06]
    elif n == 1:
        fig = pl.figure(figsize=(19.0, 18.0))
        rect = [0.10, 0.55, 0.865, 1 - 0.55 - 0.06]
    else:
        fig = pl.figure(figsize=(19.0, 16.0 + 4 * n))
        rect = [0.10, 0.64, 0.865, 1 - 0.64 - 0.06]
    axRV = pl.axes(rect)
    pl.subplots_adjust(left=0.1, top=0.865, right=0.95)
    plotindex = 1
    pltletter = ord('a')
    ax = axRV

    #Unphased plot
    ax.axhline(0, color='0.5', linestyle='--', lw=2)
    ax.plot(mplttimes, rvmod2, 'b-', linewidth=1, rasterized=False)
    ax.annotate("%s)" % chr(pltletter),
                xy=(0.01, 0.85),
                xycoords='axes fraction',
                fontsize=28,
                fontweight='bold')
    pltletter += 1
    _mtelplot(plttimes, rawresid + rvmod, rverr, cpspost.likelihood.telvec, ax,
              telfmts)
    ax.set_xlim(min(plttimes) - 0.01 * dt, max(plttimes) + 0.01 * dt)

    pl.setp(axRV.get_xticklabels(), visible=False)

    # Years on upper axis
    axyrs = axRV.twiny()
    axyrs.set_xlim(min(plttimes) - 0.01 * dt, max(plttimes) + 0.01 * dt)
    #yrticklocs = [date2jd(datetime(y, 1, 1, 0, 0, 0))-e for y in [1998, 2002, 2006, 2010, 2014]]
    yrticklocs = []
    yrticklabels = []
    for y in [1988, 1992, 1996, 2000, 2004, 2008, 2012, 2016]:
        jd = Time("%d-01-01T00:00:00" % y, format='isot', scale='utc').jd - e
        if jd > ax.get_xlim()[0] and jd < ax.get_xlim()[1]:
            yrticklocs.append(jd)
            yrticklabels.append("%d" % y)
    axyrs.set_xticks(yrticklocs)
    axyrs.set_xticklabels(yrticklabels)
    if len(yrticklabels) > 0:
        pl.xlabel('Year')
        axyrs.grid(False)

    if not yscale_auto:
        ax.set_ylim(-yscale_sigma * np.std(rawresid + rvmod),
                    yscale_sigma * np.std(rawresid + rvmod))
    ax.set_ylabel('RV [m s$^{-1}$]')
    ticks = ax.yaxis.get_majorticklocs()
    ax.yaxis.set_ticks(ticks[1:])

    divider = make_axes_locatable(axRV)
    axResid = divider.append_axes("bottom",
                                  size="50%",
                                  pad=0.0,
                                  sharex=axRV,
                                  sharey=None)
    ax = axResid

    #Residuals
    ax.plot(mplttimes, slope, 'b-', linewidth=3)
    ax.annotate("%s)" % chr(pltletter),
                xy=(0.01, 0.80),
                xycoords='axes fraction',
                fontsize=28,
                fontweight='bold')
    pltletter += 1

    _mtelplot(plttimes, resid, rverr, cpspost.likelihood.telvec, ax, telfmts)
    if not yscale_auto:
        ax.set_ylim(-yscale_sigma * np.std(resid),
                    yscale_sigma * np.std(resid))
    ax.set_xlim(min(plttimes) - 0.01 * dt, max(plttimes) + 0.01 * dt)
    ticks = ax.yaxis.get_majorticklocs()
    ax.yaxis.set_ticks([ticks[0], 0.0, ticks[-1]])
    xticks = ax.xaxis.get_majorticklocs()
    pl.xlabel('BJD$_{\\mathrm{TDB}}$ - %d' % e)
    ax.set_ylabel('Residuals')

    # Define the locations for the axes
    axbounds = ax.get_position().bounds
    bottom = axbounds[1]
    height = (bottom - 0.15) / n
    textloc = bottom / 2
    bottom -= height + 0.05
    left, width = 0.10, 0.72

    #Phase plots
    for i in range(n):
        if nophase: break

        pnum = i + 1
        #print "Planet %d" % pnum

        rvdat = rvdat.copy()

        rvmod2 = model(rvmodt, planet_num=pnum) - slope

        modph = t_to_phase(cpspost.params, rvmodt, pnum, cat=True) - 1

        rvdat = rawresid + model(rvtimes, planet_num=pnum) - slope_low

        phase = t_to_phase(cpspost.params, rvtimes, pnum, cat=True) - 1
        p2 = t_to_phase(cpspost.params, rvtimes, pnum, cat=False) - 1

        rvdatcat = np.concatenate((rvdat, rvdat))
        rverrcat = np.concatenate((rverr, rverr))
        rvmod2cat = np.concatenate((rvmod2, rvmod2))

        bint, bindat, binerr = fastbin(phase + 1, rvdatcat, nbins=25)
        bint -= 1.0

        rect = [left, bottom - (i) * height, (left + width) + 0.045, height]
        if n == 1: rect[1] -= 0.03
        ax = pl.axes(rect)

        ax.axhline(0, color='0.5', linestyle='--', lw=2)
        ax.plot(sorted(modph), rvmod2cat[np.argsort(modph)], 'b-', linewidth=3)
        ax.annotate("%s)" % chr(pltletter),
                    xy=(0.01, 0.85),
                    xycoords='axes fraction',
                    fontsize=28,
                    fontweight='bold')
        pltletter += 1

        _mtelplot(
            phase, rvdatcat, rverrcat,
            np.concatenate(
                (cpspost.likelihood.telvec, cpspost.likelihood.telvec)), ax,
            telfmts)
        if not nobin and len(rvdat) > 10:
            ax.errorbar(bint,
                        bindat,
                        yerr=binerr,
                        fmt='ro',
                        ecolor='r',
                        markersize=msize * 2.5,
                        markeredgecolor='w',
                        markeredgewidth=2)

        pl.xlim(-0.5, 0.5)
        #meanlim = np.mean([-min(rvdat), max(rvdat)])
        #meanlim += 0.10*meanlim
        #pl.ylim(-meanlim, meanlim)
        if not yscale_auto:
            pl.ylim(-yscale_sigma * np.std(rvdatcat),
                    yscale_sigma * np.std(rvdatcat))

        letters = string.lowercase
        planetletter = letters[i + 1]
        keys = [p + str(pnum) for p in ['per', 'k', 'e']]
        labels = [cpspost.params.tex_labels().get(k, k) for k in keys]
        units = ['days', 'm s$^{-1}$', '']
        indicies = [0, 4, 2, 2]
        spacing = 0.09
        xstart = 0.65
        ystart = 0.89

        if i < n - 1:
            ticks = ax.yaxis.get_majorticklocs()
            ax.yaxis.set_ticks(ticks[1:-1])

        if n > 1:
            fig.text(0.01,
                     textloc,
                     'RV [m s$^{-1}$]',
                     rotation='vertical',
                     ha='center',
                     va='center',
                     fontsize=28)
        else:
            pl.ylabel('RV [m s$^{-1}$]')
        pl.xlabel('Phase')

        print_params = ['per', 'k', 'e']
        for l, p in enumerate(print_params):
            val = cpsparams["%s%d" % (print_params[l], pnum)]

            if uparams is None:
                anotext = '%s = %4.2f %s' % (labels[l], val, units[l])
            else:
                err = uparams["%s%d" % (print_params[l], pnum)]
                if err > 0:
                    val, err, errlow = radvel.utils.sigfig(val, err)
                    anotext = '%s = %s $\\pm$ %s %s' % (labels[l], val, err,
                                                        units[l])
                else:
                    anotext = '%s = %4.2f %s' % (labels[l], val, units[l])

            txt = ax.annotate(anotext, (xstart, ystart - l * spacing),
                              xycoords='axes fraction',
                              fontsize=28)

    if saveplot != None:
        pl.savefig(saveplot, dpi=150)
        print "RV multi-panel plot saved to %s" % saveplot
    else:
        pl.show()
Beispiel #5
0
    def plot_phasefold(self, pltletter, pnum):
        """
        Plot phased orbit plots for each planet in the fit.

        Args:
            pltletter (int): integer representation of 
                letter to be printed in the corner of the first
                phase plot.
                Ex: ord("a") gives 97, so the input should be 97.
            pnum (int): the number of the planet to be plotted. Must be
                the same as the number used to define a planet's 
                Parameter objects (e.g. 'per1' is for planet #1)

        """

        ax = pl.gca()

        if len(self.post.likelihood.x) < 20:
            self.nobin = True

        bin_fac = 1.75
        bin_markersize = bin_fac * rcParams['lines.markersize']
        bin_markeredgewidth = bin_fac * rcParams['lines.markeredgewidth']

        rvmod2 = self.model(self.rvmodt, planet_num=pnum) - self.slope
        modph = t_to_phase(self.post.params, self.rvmodt, pnum, cat=True) - 1
        rvdat = self.rawresid + self.model(self.rvtimes,
                                           planet_num=pnum) - self.slope_low
        phase = t_to_phase(self.post.params, self.rvtimes, pnum, cat=True) - 1
        rvdatcat = np.concatenate((rvdat, rvdat))
        rverrcat = np.concatenate((self.rverr, self.rverr))
        rvmod2cat = np.concatenate((rvmod2, rvmod2))
        bint, bindat, binerr = fastbin(phase + 1, rvdatcat, nbins=25)
        bint -= 1.0

        ax.axhline(
            0,
            color='0.5',
            linestyle='--',
        )
        ax.plot(sorted(modph),
                rvmod2cat[np.argsort(modph)],
                'b-',
                linewidth=self.fit_linewidth)
        plot.labelfig(pltletter)

        telcat = np.concatenate(
            (self.post.likelihood.telvec, self.post.likelihood.telvec))

        plot.mtelplot(phase,
                      rvdatcat,
                      rverrcat,
                      telcat,
                      ax,
                      telfmts=self.telfmts)
        if not self.nobin and len(rvdat) > 10:
            ax.errorbar(bint,
                        bindat,
                        yerr=binerr,
                        fmt='ro',
                        mec='w',
                        ms=bin_markersize,
                        mew=bin_markeredgewidth)

        if self.phase_limits:
            ax.set_xlim(self.phase_limits[0], self.phase_limits[1])
        else:
            ax.set_xlim(-0.5, 0.5)

        if not self.yscale_auto:
            scale = np.std(rvdatcat)
            ax.set_ylim(-self.yscale_sigma * scale, self.yscale_sigma * scale)

        keys = [p + str(pnum) for p in ['per', 'k', 'e']]

        labels = [self.post.params.tex_labels().get(k, k) for k in keys]
        if pnum < self.num_planets:
            ticks = ax.yaxis.get_majorticklocs()
            ax.yaxis.set_ticks(ticks[1:-1])

        ax.set_ylabel('RV [{ms:}]'.format(**plot.latex), weight='bold')
        ax.set_xlabel('Phase', weight='bold')

        print_params = ['per', 'k', 'e']
        units = {'per': 'days', 'k': plot.latex['ms'], 'e': ''}

        anotext = []
        for l, p in enumerate(print_params):
            val = self.post.params["%s%d" % (print_params[l], pnum)].value

            if self.uparams is None:
                _anotext = '$\\mathregular{%s}$ = %4.2f %s' % (
                    labels[l].replace("$", ""), val, units[p])
            else:
                if hasattr(self.post, 'medparams'):
                    val = self.post.medparams["%s%d" % (print_params[l], pnum)]
                else:
                    print("WARNING: medparams attribute not found in " +
                          "posterior object will annotate with " +
                          "max-likelihood values and reported uncertainties " +
                          "may not be appropriate.")
                err = self.uparams["%s%d" % (print_params[l], pnum)]
                if err > 0:
                    val, err, errlow = sigfig(val, err)
                    _anotext = '$\\mathregular{%s}$ = %s $\\mathregular{\\pm}$ %s %s' \
                               % (labels[l].replace("$", ""), val, err, units[p])
                else:
                    _anotext = '$\\mathregular{%s}$ = %4.2f %s' % (
                        labels[l].replace("$", ""), val, units[p])

            anotext += [_anotext]

        anotext = '\n'.join(anotext)
        plot.add_anchored(anotext,
                          loc=1,
                          frameon=True,
                          prop=dict(size=self.phasetext_size, weight='bold'),
                          bbox=dict(ec='none', fc='w', alpha=0.8))
Beispiel #6
0
def rv_multipanel_plot(post, saveplot=None, telfmts={}, nobin=False, 
                       yscale_auto=False, yscale_sigma=3.0, nophase=False, 
                       epoch=2450000, uparams=None, phase_ncols=None, 
                       phase_nrows=None, legend=True, rv_phase_space=0.08):
    """Multi-panel RV plot to display model using post.params orbital
    parameters.

    Args:
        post (radvel.Posterior): Radvel posterior object. The model
            plotted will be generated from post.params
        saveplot (string, optional): Name of output file, will show as
             interactive matplotlib window if not defined.
        nobin (bool, optional): If True do not show binned data on
             phase plots. Will default to True if total number of
             measurements is less then 20.
        yscale_auto (bool, optional): Use matplotlib auto y-axis
             scaling (default: False)
        yscale_sigma (float, optional): Scale y-axis limits to be +/-
             yscale_sigma*(RMS of data plotted) if yscale_auto==False
        telfmts (dict, optional): dictionary of dictionaries mapping
             instrument code to plotting format code.
        nophase (bool, optional): Will omit phase-folded plots if true
        epoch (float, optional): Subtract this value from the time axis for
            more compact axis labels (default: 245000)
        uparams (dict, optional): parameter uncertainties, must
           contain 'per', 'k', and 'e' keys.
        phase_ncols (int, optional): number of columns in the phase
            folded plots. Default behavior is 1.
        phase_nrows (int, optional): number of columns in the phase
            folded plots. Default is nplanets.
        legend (bool, optional): include legend on plot? (default: True)
        rv_phase_space (float, optional): verticle space between rv
            plot and phase-folded plots (in units of fraction of
            figure height)

    Returns:
        figure: current matplotlib figure object
        list: list of axis objects

    """
    figwidth = 7.5  # spans a page with 0.5in margins
    phasefac = 1.4
    ax_rv_height = figwidth * 0.6
    ax_phase_height = ax_rv_height / phasefac
    bin_fac = 1.75
    bin_markersize = bin_fac * rcParams['lines.markersize']
    bin_markeredgewidth = bin_fac * rcParams['lines.markeredgewidth']
    fit_linewidth = 2.0

    cpspost = copy.deepcopy(post) 
    model = cpspost.likelihood.model
    cpsparams = post.params.basis.to_cps(post.params)
    cpspost.params.update(cpsparams)
    rvtimes = cpspost.likelihood.x
    rvdat = cpspost.likelihood.y
    rverr = cpspost.likelihood.errorbars()
    num_planets = model.num_planets

    if nophase:
        num_planets = 1
        periods = [max(rvtimes) - min(rvtimes)]

    if phase_ncols is None:
        phase_ncols = 1
    if phase_nrows is None:
        phase_nrows = num_planets

    ax_phase_height /= phase_ncols
        
    e = epoch
    if len(post.likelihood.x) < 20: 
        nobin = True
    
    if saveplot != None: 
        resolution = 10000
    else: 
        resolution = 2000

    if isinstance(cpspost.likelihood, radvel.likelihood.CompositeLikelihood):
        like_list = cpspost.likelihood.like_list
    else:
        like_list = [ cpspost.likelihood ]
    
    if not nophase:
        periods = []
        for i in range(num_planets):
            periods.append(cpsparams['per%d' % (i+1)])
            
    longp = max(periods)
    shortp = min(periods)
        
    dt = max(rvtimes) - min(rvtimes)
    rvmodt = np.linspace(
        min(rvtimes) - 0.05 * dt, max(rvtimes) + 0.05 * dt + longp,
        int(resolution)
    )
    
    rvmod2 = model(rvmodt)
    rvmod = model(rvtimes)

    if ((rvtimes - e) < -2.4e6).any():
        plttimes = rvtimes
        mplttimes = rvmodt
    elif e == 0:
        e = 2450000
        plttimes = rvtimes - e
        mplttimes = rvmodt - e
    else:
        plttimes = rvtimes - e
        mplttimes = rvmodt - e

    rawresid = cpspost.likelihood.residuals()
    resid = (
        rawresid + cpsparams['dvdt']*(rvtimes-model.time_base) 
        + cpsparams['curv']*(rvtimes-model.time_base)**2
    )
    slope = (
        cpsparams['dvdt'] * (rvmodt-model.time_base) 
        + cpsparams['curv'] * (rvmodt-model.time_base)**2
    )
    slope_low = (
        cpsparams['dvdt'] * (rvtimes-model.time_base) 
        + cpsparams['curv'] * (rvtimes-model.time_base)**2
    )

    # Provision figure
    figheight = ax_rv_height + ax_phase_height * phase_nrows
    divide = 1 - ax_rv_height / figheight
    fig = pl.figure(figsize=(figwidth, figheight))
    fig.subplots_adjust(left=0.12, right=0.95)
    gs_rv = gridspec.GridSpec(1, 1)
    gs_rv.update(left=0.12, right=0.93, top=0.93,
                 bottom=divide+rv_phase_space*0.5)
    gs_phase = gridspec.GridSpec(phase_nrows, phase_ncols)

    if phase_ncols == 1:
        gs_phase.update(left=0.12, right=0.93,
                        top=divide - rv_phase_space * 0.5,
                        bottom=0.07, hspace=0.003)
    else:
        gs_phase.update(left=0.12, right=0.93,
                        top=divide - rv_phase_space * 0.5,
                        bottom=0.07, hspace=0.25, wspace=0.25)

    axL = []
    axRV = pl.subplot(gs_rv[0, 0])
    plotindex = 1
    pltletter = ord('a')
    ax = axRV

    axL += [axRV]
   
    # Unphased plot
    ax.axhline(0, color='0.5', linestyle='--')
    ax.plot(mplttimes,rvmod2,'b-', rasterized=False, lw=0.1)

    def labelfig(ax, pltletter):
        text = "{})".format(chr(pltletter))
        add_anchored(
            text, loc=2, prop=dict(fontweight='bold', size='large'),
            frameon=False
        )

    labelfig(ax, pltletter)

    pltletter += 1
    _mtelplot(
        plttimes, rawresid+rvmod, rverr, cpspost.likelihood.telvec, ax, telfmts
    )
    ax.set_xlim(min(plttimes)-0.01*dt, max(plttimes)+0.01*dt)
    
    pl.setp(axRV.get_xticklabels(), visible=False)

    # Legend
    if legend:
        pl.legend(numpoints=1, fontsize='x-small', loc='best')
    
    # Years on upper axis
    axyrs = axRV.twiny()
    # axyrs.set_xlim(min(plttimes)-0.01*dt,max(plttimes)+0.01*dt)

    xl = np.array(list(ax.get_xlim())) + e
    decimalyear = Time(xl,format='jd',scale='utc').decimalyear
    axyrs.plot(decimalyear, decimalyear)
    axyrs.get_xaxis().get_major_formatter().set_useOffset(False)
    axyrs.set_xlim(*decimalyear)
    axyrs.set_xlabel('Year', fontweight='bold')
    # axyrs.xaxis.set_major_locator(MaxNLocator(8))

    if not yscale_auto: 
        scale = np.std(rawresid+rvmod)
        ax.set_ylim(-yscale_sigma * scale , yscale_sigma * scale)

    ax.set_ylabel('RV [{ms:}]'.format(**latex), weight='bold')
    ticks = ax.yaxis.get_majorticklocs()
    ax.yaxis.set_ticks(ticks[1:])

    divider = make_axes_locatable(axRV)
    axResid = divider.append_axes(
        "bottom",size="50%", pad=0.0, sharex=axRV, sharey=None
    )
    ax = axResid
    axL += [axResid]

    # Residuals
    ax.plot(mplttimes, slope, 'b-', lw=fit_linewidth)

    labelfig(ax, pltletter)

    pltletter += 1

    _mtelplot(plttimes, resid, rverr, cpspost.likelihood.telvec, ax, telfmts)
    if not yscale_auto: 
        scale = np.std(resid)
        ax.set_ylim(-yscale_sigma * scale, yscale_sigma * scale)

    ax.set_xlim(min(plttimes)-0.01*dt,max(plttimes)+0.01*dt)
    ticks = ax.yaxis.get_majorticklocs()
    ax.yaxis.set_ticks([ticks[0],0.0,ticks[-1]])
    xticks = ax.xaxis.get_majorticklocs()
    pl.xlabel('JD - {:d}'.format(int(np.round(e))), weight='bold')
    ax.set_ylabel('Residuals', weight='bold')
    ax.yaxis.set_major_locator(MaxNLocator(5,prune='both'))
    
    # Define the locations for the axes
    axbounds = ax.get_position().bounds
    bottom = axbounds[1]
    height = (bottom - 0.15) / num_planets
    textloc = bottom / 2
    bottom -= height + 0.05
    left, width = 0.10, 0.72

    #Phase plots
    for i in range(num_planets):
        if nophase: break
        
        pnum = i+1
        #print("Planet %d" % pnum)

        rvdat = rvdat.copy()
        rvmod2 = model(rvmodt, planet_num=pnum) - slope
        modph = t_to_phase(cpspost.params, rvmodt, pnum, cat=True) - 1
        rvdat = rawresid + model(rvtimes, planet_num=pnum) - slope_low
        phase = t_to_phase(cpspost.params, rvtimes, pnum, cat=True) - 1
        p2 = t_to_phase(cpspost.params, rvtimes, pnum, cat=False) - 1
        rvdatcat = np.concatenate((rvdat,rvdat))
        rverrcat = np.concatenate((rverr,rverr))
        rvmod2cat = np.concatenate((rvmod2,rvmod2))
        bint, bindat, binerr = fastbin(phase+1, rvdatcat, nbins=25)
        bint -= 1.0

        i_row = i / phase_ncols
        i_col = i - i_row * phase_ncols
        ax = pl.subplot(gs_phase[i_row, i_col])
        axL += [ax]

        ax.axhline(0, color='0.5', linestyle='--', )
        ax.plot(sorted(modph),rvmod2cat[np.argsort(modph)],'b-',linewidth=fit_linewidth)
        labelfig(ax,pltletter)

        pltletter += 1
        telcat = np.concatenate((cpspost.likelihood.telvec,cpspost.likelihood.telvec))

        _mtelplot(phase,rvdatcat, rverrcat, telcat, ax, telfmts)
        if not nobin and len(rvdat) > 10: 
            ax.errorbar(
                bint, bindat, yerr=binerr, fmt='ro',mec='w', ms=bin_markersize, 
                mew=bin_markeredgewidth
            )

        pl.xlim(-0.5,0.5)

        if not yscale_auto: 
            scale = np.std(rvdatcat)
            pl.ylim(-yscale_sigma*scale, yscale_sigma*scale )
        
        letters = string.lowercase
        planetletter = letters[i+1]
        keys = [p+str(pnum) for p in ['per', 'k', 'e'] ]
        labels = [cpspost.params.tex_labels().get(k, k) for k in keys]
        if i < num_planets-1:
            ticks = ax.yaxis.get_majorticklocs()
            ax.yaxis.set_ticks(ticks[1:-1])

        pl.ylabel('RV [{ms:}]'.format(**latex), weight='bold')
        pl.xlabel('Phase', weight='bold')

        print_params = ['per', 'k', 'e']
        units = {'per':'days','k':latex['ms'],'e':''}

        anotext = []
        for l, p in enumerate(print_params):
            val = cpsparams["%s%d" % (print_params[l],pnum)]
            
            if uparams is None:
                _anotext = '$\\mathregular{%s}$ = %4.2f %s' % (labels[l].replace("$",""), val, units[p])
            else:
                if hasattr(post, 'medparams'):
                    val = post.medparams["%s%d" % (print_params[l],pnum)]
                else:
                    print("WARNING: medparams attribute not found in " +
                          "posterior object will annotate with " +
                          "max-likelihood values and reported uncertainties " +
                          "may not be appropriate.")
                err = uparams["%s%d" % (print_params[l],pnum)]
                if err > 0:
                    val, err, errlow = radvel.utils.sigfig(val, err)
                    _anotext = '$\\mathregular{%s}$ = %s $\\mathregular{\\pm}$ %s %s' % (labels[l].replace("$",""), val, err, units[p])
                else:
                    _anotext = '$\\mathregular{%s}$ = %4.2f %s' % (labels[l].replace("$",""), val, units[p])

            anotext += [_anotext] 

        anotext = '\n'.join(anotext)
        add_anchored(
            anotext, loc=1, frameon=True, prop=dict(size='large', weight='bold'),
            bbox=dict(ec='none', fc='w', alpha=0.8)
        )

    if saveplot != None:
        pl.savefig(saveplot,dpi=150)
        print("RV multi-panel plot saved to %s" % saveplot)
        
    return fig, axL
Beispiel #7
0
def rv_multipanel_plot(post,
                       saveplot=None,
                       telfmts={},
                       nobin=False,
                       yscale_auto=False,
                       yscale_sigma=3.0,
                       nophase=False,
                       epoch=2450000,
                       uparams=None,
                       phase_ncols=None,
                       phase_nrows=None,
                       legend=True,
                       legend_fontsize='x-small',
                       rv_phase_space=0.08,
                       phase_limits=[],
                       subtract_gp_mean_model=False,
                       plot_likelihoods_separately=True,
                       subtract_orbit_model=False):
    """Multi-panel RV plot to display model using post.params orbital
    parameters.

    Args:
        post (radvel.Posterior): Radvel posterior object. The model
            plotted will be generated from post.params
        saveplot (string, optional): Name of output file, will show as
             interactive matplotlib window if not defined.
        nobin (bool, optional): If True do not show binned data on
             phase plots. Will default to True if total number of
             measurements is less then 20.
        yscale_auto (bool, optional): Use matplotlib auto y-axis
             scaling (default: False)
        yscale_sigma (float, optional): Scale y-axis limits to be +/-
             yscale_sigma*(RMS of data plotted) if yscale_auto==False
        telfmts (dict, optional): dictionary of dictionaries mapping
             instrument code to plotting format code.
        nophase (bool, optional): Will omit phase-folded plots if true
        epoch (float, optional): Subtract this value from the time axis for
            more compact axis labels (default: 245000)
        uparams (dict, optional): parameter uncertainties, must
           contain 'per', 'k', and 'e' keys.
        phase_ncols (int, optional): number of columns in the phase
            folded plots. Default behavior is 1.
        phase_nrows (int, optional): number of columns in the phase
            folded plots. Default is nplanets.
        legend (bool, optional): include legend on plot? (default: True)
        legend_fontsize (str, optional): fontsize parameter to be passed
            to matplotlib.legend. Choose from {'xx-small', 'x-small', 
            'small', 'medium', 'large', 'x-large', 'xx-large'}. 
            (default: 'x-small')
        rv_phase_space (float, optional): verticle space between rv
            plot and phase-folded plots (in units of fraction of
            figure height)
        phase_limits (list, optional): two element list specifying 
            pyplot.xlim bounds for phase-folded array. Useful for
            partial orbits.
        subtract_gp_mean_model (bool, optional): if True, subtract the Gaussian
            process mean max likelihood model from the data and the
            model when plotting the results. 
        plot_likelihoods_separately (bool, optional): if True, plot a separate
            panel for each Likelihood object.
        subtract_orbit_model (bool, optional): if True, subtract the best-fit
            orbit model from the data and the model when plotting 
            the results. Useful for seeing the structure of correlated
            noise in the data.

    Returns:
        figure: current matplotlib figure object
        list: list of axis objects
    """
    figwidth = 7.5  # spans a page with 0.5in margins
    phasefac = 1.4
    ax_rv_height = figwidth * 0.6
    ax_phase_height = ax_rv_height / phasefac
    bin_fac = 1.75
    bin_markersize = bin_fac * rcParams['lines.markersize']
    bin_markeredgewidth = bin_fac * rcParams['lines.markeredgewidth']
    fit_linewidth = 2.0

    synthpost = copy.deepcopy(post)
    model = synthpost.likelihood.model
    synthparams = post.params.basis.to_synth(post.params)
    synthpost.params.update(synthparams)
    rvtimes = synthpost.likelihood.x
    rverr = synthpost.likelihood.errorbars()
    num_planets = model.num_planets

    if nophase:
        num_planets = 1
        periods = [max(rvtimes) - min(rvtimes)]

    if phase_ncols is None:
        phase_ncols = 1
    if phase_nrows is None:
        phase_nrows = num_planets

    ax_phase_height /= phase_ncols

    e = epoch
    if len(post.likelihood.x) < 20:
        nobin = True

    if saveplot is not None:
        resolution = 10000
    else:
        resolution = 2000

    if isinstance(synthpost.likelihood, radvel.likelihood.CompositeLikelihood):
        like_list = synthpost.likelihood.like_list
    else:
        like_list = [synthpost.likelihood]

    if not nophase:
        periods = []
        for i in range(num_planets):
            periods.append(synthparams['per%d' % (i + 1)].value)

    longp = max(periods)

    dt = max(rvtimes) - min(rvtimes)
    rvmodt = np.linspace(
        min(rvtimes) - 0.05 * dt,
        max(rvtimes) + 0.05 * dt + longp, int(resolution))

    rvmod2 = model(rvmodt)
    rvmod = model(rvtimes)

    if ((rvtimes - e) < -2.4e6).any():
        plttimes = rvtimes
        mplttimes = rvmodt
    elif e == 0:
        e = 2450000
        plttimes = rvtimes - e
        mplttimes = rvmodt - e
    else:
        plttimes = rvtimes - e
        mplttimes = rvmodt - e

    rawresid = synthpost.likelihood.residuals()
    resid = (rawresid + synthparams['dvdt'].value *
             (rvtimes - model.time_base) + synthparams['curv'].value *
             (rvtimes - model.time_base)**2)
    slope = (synthparams['dvdt'].value * (rvmodt - model.time_base) +
             synthparams['curv'].value * (rvmodt - model.time_base)**2)
    slope_low = (synthparams['dvdt'].value * (rvtimes - model.time_base) +
                 synthparams['curv'].value * (rvtimes - model.time_base)**2)

    # Provision figure
    figheight = ax_rv_height + ax_phase_height * phase_nrows
    divide = 1 - ax_rv_height / figheight
    fig = pl.figure(figsize=(figwidth, figheight))
    fig.subplots_adjust(left=0.12, right=0.95)
    gs_rv = gridspec.GridSpec(1, 1)
    gs_rv.update(left=0.12,
                 right=0.93,
                 top=0.93,
                 bottom=divide + rv_phase_space * 0.5)
    gs_phase = gridspec.GridSpec(phase_nrows, phase_ncols)

    if phase_ncols == 1:
        gs_phase.update(left=0.12,
                        right=0.93,
                        top=divide - rv_phase_space * 0.5,
                        bottom=0.07,
                        hspace=0.003)
    else:
        gs_phase.update(left=0.12,
                        right=0.93,
                        top=divide - rv_phase_space * 0.5,
                        bottom=0.07,
                        hspace=0.25,
                        wspace=0.25)

    ax_list = []
    ax_rv = pl.subplot(gs_rv[0, 0])
    pltletter = ord('a')
    ax = ax_rv

    ax_list += [ax_rv]

    ax.axhline(0, color='0.5', linestyle='--')

    # Default formatting
    lw = 0.01
    ci = 0
    default_colors = ['orange', 'purple', 'magenta', 'pink']

    numdatapoints = 0
    for like in like_list:
        if isinstance(like, radvel.likelihood.GPLikelihood):
            gp_mean, _ = like.predict(like.x)
            if not subtract_gp_mean_model:
                rvmod[numdatapoints:numdatapoints + len(like.x)] += gp_mean
        numdatapoints += len(like.x)

    for like in like_list:
        if isinstance(like, radvel.likelihood.GPLikelihood):

            t = like.suffix

            kw = dict(fmt='o',
                      capsize=0,
                      mew=0,
                      ecolor='0.6',
                      lw=lw,
                      color=default_colors[ci],
                      label=t)

            # If not explicit format set, look among default formats
            telfmt = {}
            if t not in telfmts and t in telfmts_default:
                telfmt = telfmts_default[t]
            if t in telfmts:
                telfmt = telfmts[t]
                print(telfmt)
            if t not in telfmts and t not in telfmts_default:
                ci += 1
            for k in telfmt:
                kw[k] = telfmt[k]

            xpred = np.linspace(np.min(like.x), np.max(like.x), num=int(3e3))
            gpmu, stddev = like.predict(xpred)

            if ((xpred - e) < -2.4e6).any():
                pass
            elif e == 0:
                e = 2450000
                xpred = xpred - e
            else:
                xpred = xpred - e

            orbit_model = like.model(xpred)
            if subtract_gp_mean_model:
                gpmu = 0.
            if subtract_orbit_model:
                orbit_model = 0.
            ax.fill_between(xpred,
                            gpmu + orbit_model - stddev,
                            gpmu + orbit_model + stddev,
                            color=kw['color'],
                            alpha=0.5,
                            lw=0)
            ax.plot(xpred, gpmu + orbit_model, 'b-', rasterized=False, lw=0.1)

        else:
            # Unphased plot
            orbit_model = rvmod2
            if subtract_orbit_model:
                orbit_model = 0.
            ax.plot(mplttimes, orbit_model, 'b-', rasterized=False, lw=0.1)

    def labelfig(letter):
        text = "{})".format(chr(letter))
        add_anchored(text,
                     loc=2,
                     prop=dict(fontweight='bold', size='large'),
                     frameon=False)

    labelfig(pltletter)

    pltletter += 1

    if subtract_orbit_model:
        _mtelplot(
            # data = residuals (best fit model subtracted out)
            plttimes,
            rawresid,
            rverr,
            synthpost.likelihood.telvec,
            ax,
            telfmts)
    else:
        _mtelplot(
            # data = residuals + best fit model
            plttimes,
            rawresid + rvmod,
            rverr,
            synthpost.likelihood.telvec,
            ax,
            telfmts)
    ax.set_xlim(min(plttimes) - 0.01 * dt, max(plttimes) + 0.01 * dt)

    pl.setp(ax_rv.get_xticklabels(), visible=False)

    # Legend
    if legend:
        pl.legend(numpoints=1, fontsize=legend_fontsize, loc='best')

    # Years on upper axis
    axyrs = ax_rv.twiny()
    # axyrs.set_xlim(min(plttimes)-0.01*dt,max(plttimes)+0.01*dt)

    xl = np.array(list(ax.get_xlim())) + e
    decimalyear = Time(xl, format='jd', scale='utc').decimalyear
    axyrs.plot(decimalyear, decimalyear)
    axyrs.get_xaxis().get_major_formatter().set_useOffset(False)
    axyrs.set_xlim(*decimalyear)
    axyrs.set_xlabel('Year', fontweight='bold')
    # axyrs.xaxis.set_major_locator(MaxNLocator(8))

    if not yscale_auto:
        scale = np.std(rawresid + rvmod)
        ax.set_ylim(-yscale_sigma * scale, yscale_sigma * scale)

    ax.set_ylabel('RV [{ms:}]'.format(**latex), weight='bold')
    ticks = ax.yaxis.get_majorticklocs()
    ax.yaxis.set_ticks(ticks[1:])

    divider = make_axes_locatable(ax_rv)
    ax_resid = divider.append_axes("bottom",
                                   size="50%",
                                   pad=0.0,
                                   sharex=ax_rv,
                                   sharey=None)
    ax = ax_resid
    ax_list += [ax_resid]

    # Residuals
    ax.plot(mplttimes, slope, 'b-', lw=fit_linewidth)

    labelfig(pltletter)

    pltletter += 1

    _mtelplot(plttimes, resid, rverr, synthpost.likelihood.telvec, ax, telfmts)
    if not yscale_auto:
        scale = np.std(resid)
        ax.set_ylim(-yscale_sigma * scale, yscale_sigma * scale)

    ax.set_xlim(min(plttimes) - 0.01 * dt, max(plttimes) + 0.01 * dt)
    ticks = ax.yaxis.get_majorticklocs()
    ax.yaxis.set_ticks([ticks[0], 0.0, ticks[-1]])
    pl.xlabel('JD - {:d}'.format(int(np.round(e))), weight='bold')
    ax.set_ylabel('Residuals', weight='bold')
    ax.yaxis.set_major_locator(MaxNLocator(5, prune='both'))

    # Define the locations for the axes
    axbounds = ax.get_position().bounds
    bottom = axbounds[1]
    height = (bottom - 0.15) / num_planets
    bottom -= height + 0.05

    # Phase plots
    for i in range(num_planets):
        if nophase:
            break

        pnum = i + 1

        rvmod2 = model(rvmodt, planet_num=pnum) - slope
        modph = t_to_phase(synthpost.params, rvmodt, pnum, cat=True) - 1
        rvdat = rawresid + model(rvtimes, planet_num=pnum) - slope_low
        phase = t_to_phase(synthpost.params, rvtimes, pnum, cat=True) - 1
        rvdatcat = np.concatenate((rvdat, rvdat))
        rverrcat = np.concatenate((rverr, rverr))
        rvmod2cat = np.concatenate((rvmod2, rvmod2))
        bint, bindat, binerr = fastbin(phase + 1, rvdatcat, nbins=25)
        bint -= 1.0

        i_row = int(i / phase_ncols)
        i_col = int(i - i_row * phase_ncols)
        ax = pl.subplot(gs_phase[i_row, i_col])
        ax_list += [ax]

        ax.axhline(
            0,
            color='0.5',
            linestyle='--',
        )
        ax.plot(sorted(modph),
                rvmod2cat[np.argsort(modph)],
                'b-',
                linewidth=fit_linewidth)
        labelfig(pltletter)

        pltletter += 1
        telcat = np.concatenate(
            (synthpost.likelihood.telvec, synthpost.likelihood.telvec))

        _mtelplot(phase, rvdatcat, rverrcat, telcat, ax, telfmts)
        if not nobin and len(rvdat) > 10:
            ax.errorbar(bint,
                        bindat,
                        yerr=binerr,
                        fmt='ro',
                        mec='w',
                        ms=bin_markersize,
                        mew=bin_markeredgewidth)

        if not phase_limits:
            pl.xlim(-0.5, 0.5)
        else:
            pl.xlim(phase_limits[0], phase_limits[1])

        if not yscale_auto:
            scale = np.std(rvdatcat)
            pl.ylim(-yscale_sigma * scale, yscale_sigma * scale)

        keys = [p + str(pnum) for p in ['per', 'k', 'e']]
        labels = [synthpost.params.tex_labels().get(k, k) for k in keys]
        if i < num_planets - 1:
            ticks = ax.yaxis.get_majorticklocs()
            ax.yaxis.set_ticks(ticks[1:-1])

        pl.ylabel('RV [{ms:}]'.format(**latex), weight='bold')
        pl.xlabel('Phase', weight='bold')

        print_params = ['per', 'k', 'e']
        units = {'per': 'days', 'k': latex['ms'], 'e': ''}

        anotext = []
        for l, p in enumerate(print_params):
            val = synthparams["%s%d" % (print_params[l], pnum)].value

            if uparams is None:
                _anotext = '$\\mathregular{%s}$ = %4.2f %s' % (
                    labels[l].replace("$", ""), val, units[p])
            else:
                if hasattr(post, 'medparams'):
                    val = post.medparams["%s%d" % (print_params[l], pnum)]
                else:
                    print("WARNING: medparams attribute not found in " +
                          "posterior object will annotate with " +
                          "max-likelihood values and reported uncertainties " +
                          "may not be appropriate.")
                err = uparams["%s%d" % (print_params[l], pnum)]
                if err > 0:
                    val, err, errlow = radvel.utils.sigfig(val, err)
                    _anotext = '$\\mathregular{%s}$ = %s $\\mathregular{\\pm}$ %s %s' \
                               % (labels[l].replace("$", ""), val, err, units[p])
                else:
                    _anotext = '$\\mathregular{%s}$ = %4.2f %s' % (
                        labels[l].replace("$", ""), val, units[p])

            anotext += [_anotext]

        anotext = '\n'.join(anotext)
        add_anchored(anotext,
                     loc=1,
                     frameon=True,
                     prop=dict(size='large', weight='bold'),
                     bbox=dict(ec='none', fc='w', alpha=0.8))

    if saveplot is not None:
        pl.savefig(saveplot, dpi=150)
        print("RV multi-panel plot saved to %s" % saveplot)

    return fig, ax_list