def __init__(self, name, single=False, sig_RpRs=0.001, **kwargs): """Instantiate the pysyzygy model.""" # The planet/transit model ID assert type(name) is str, "Arg `name` must be a string." self.name = name # Skip if pysyzygy not installed if ps is None: return # The transit model self._transit = ps.Transit(**kwargs) # Compute the depth times = kwargs.get('times', None) if times is not None: self.t0 = times[0] else: self.t0 = kwargs.get('t0', 0.) self.per = kwargs.get('per', 10.) self.single = single self.depth = (1. - self._transit([self.t0]))[0] # Approximate variance on the depth self.var_depth = (2 * sig_RpRs) ** 2 # Save the kwargs self.params = kwargs
def Transit(time, t0=0., dur=0.1, per=3.56789, depth=0.001, **kwargs): ''' A `Mandel-Agol <http://adsabs.harvard.edu/abs/2002ApJ...580L.171M>`_ transit model, but with the depth and the duration as primary input variables. :param numpy.ndarray time: The time array :param float t0: The time of first transit in units of \ :py:obj:`BJD` - 2454833. :param float dur: The transit duration in days. Don't go too crazy on \ this one -- very small or very large values will break the \ inverter. Default 0.1 :param float per: The orbital period in days. Default 3.56789 :param float depth: The fractional transit depth. Default 0.001 :param dict kwargs: Any additional keyword arguments, passed directly \ to :py:func:`pysyzygy.Transit` :returns tmod: The transit model evaluated at the same times as the \ :py:obj:`time` array ''' if ps is None: raise Exception("Unable to import `pysyzygy`.") # Note that rhos can affect RpRs, so we should really do this iteratively, # but the effect is pretty negligible! RpRs = Get_RpRs(depth, t0=t0, per=per, **kwargs) rhos = Get_rhos(dur, t0=t0, per=per, **kwargs) return ps.Transit(t0=t0, per=per, RpRs=RpRs, rhos=rhos, **kwargs)(time)
def Dur(rhos, **kwargs): t0 = kwargs.get('t0', 0.) time = np.linspace(t0 - 0.5, t0 + 0.5, 1000) try: t = time[np.where(ps.Transit(rhos=rhos, **kwargs)(time) < 1)] except: return 0. return t[-1] - t[0]
def flux(self, time, planets=None, cadence='lc'): ''' ''' # Ensure it's a list if planets is None: planets = self.planets elif type(planets) is str: planets = [planets] # Compute flux for each planet flux = np.ones_like(time) for planet in planets: if cadence == 'lc': model = ps.Transit(per=self.period[planet], b=self.b[planet], RpRs=self.RpRs[planet], t0=self.t0[planet], rhos=self.rhos, ecc=self.ecc[planet], w=self.w[planet] * np.pi / 180., u1=self.u1, u2=self.u2, times=self.times[planet]) else: model = ps.Transit(per=self.period[planet], b=self.b[planet], RpRs=self.RpRs[planet], t0=self.t0[planet], rhos=self.rhos, ecc=self.ecc[planet], w=self.w[planet] * np.pi / 180., u1=self.u1, u2=self.u2, times=self.times[planet], exptime=ps.KEPSHRTCAD) flux *= model(time) return flux
def __init__(self, depth=1, dur=0.1, **kwargs): """Initialize the pysyzygy model.""" if ps is None: return kwargs.pop('t0', None) kwargs.pop('times', None) # Update kwargs with correct duration kwargs.update({'per': 3.56789}) kwargs.update({'rhos': Get_rhos(dur, **kwargs)}) # Transit window size w/ padding window = dur * 3 t = np.linspace(-window / 2, window / 2, 5000) # Construct a transit model trn = ps.Transit(t0=0., **kwargs) transit_model = trn(t) transit_model -= 1 transit_model *= depth / (1 - trn([0.])[0]) self.x = t self.y = transit_model
def negll(x): ''' ''' RpRs, bcirc, q1, q2 = x psm = ps.Transit(per=per, q1=q1, q2=q2, RpRs=RpRs, rhos=rhos, tN=tN, ecw=0., esw=0., bcirc=bcirc, MpMs=0.) ll = 0 # Loop over all quarters for q in inp.quarters: # Empty? if len(pdata[q]['time']) == 0: continue # Info for this quarter crwd = tdata[q]['crwd'] c = tdata[q]['dvec'][iPLD:] x = tdata[q]['dvec'][:iPLD] inp.kernel.pars = x gp = george.GP(inp.kernel) # Loop over all transits for time, fpix, perr in zip(tdata[q]['time'], tdata[q]['fpix'], tdata[q]['perr']): # Compute the transit model try: tmod = psm(time, 'binned') except Exception as e: if inp.debug: print("Transit optimization exception:", str(e)) return 1.e20 # Compute the PLD model pmod, ypld, yerr = PLDFlux(c, fpix, perr, tmod, crowding=crwd) # Compute the GP model try: gp.compute(time, yerr) except Exception as e: if inp.debug: print("Transit optimization exception:", str(e)) return 1.e20 ll += gp.lnlikelihood(ypld) if inp.debug: print("Likelihood: ", ll) return -ll
def ComputePLD(input_file=None, clobber=False): ''' ''' # Load inputs inp = Input(input_file) detpath = os.path.join(inp.datadir, str(inp.id), '_detrend') iPLD = len(inp.kernel.pars) # Have we already detrended? pdata = GetData(inp.id, data_type='prc', datadir=inp.datadir) tdata = GetData(inp.id, data_type='trn', datadir=inp.datadir) if not (inp.clobber or clobber): try: for q in pdata: if len(pdata[q]['time']) == 0: continue if len(pdata[q]['pmod']) == 0: raise ValueError("Pixel model list is empty.") # Data is already detrended if not inp.quiet: print("Using existing PLD info.") return True except ValueError: # We're going to have to compute pass # Get some info info = DownloadInfo(inp.id, inp.dataset, trninfo=inp.trninfo, inject=inp.inject, datadir=inp.datadir, clobber=False, ttvs=inp.ttvs, pad=inp.padbkg) per = info['per'] rhos = info['rhos'] tN = info['tN'] # Optimize transit model if len(tN): if not inp.quiet: print("Computing approximate transit model...") # This lets us approximately solve for RpRs, bcirc, q1, q2 def negll(x): ''' ''' RpRs, bcirc, q1, q2 = x psm = ps.Transit(per=per, q1=q1, q2=q2, RpRs=RpRs, rhos=rhos, tN=tN, ecw=0., esw=0., bcirc=bcirc, MpMs=0.) ll = 0 # Loop over all quarters for q in inp.quarters: # Empty? if len(pdata[q]['time']) == 0: continue # Info for this quarter crwd = tdata[q]['crwd'] c = tdata[q]['dvec'][iPLD:] x = tdata[q]['dvec'][:iPLD] inp.kernel.pars = x gp = george.GP(inp.kernel) # Loop over all transits for time, fpix, perr in zip(tdata[q]['time'], tdata[q]['fpix'], tdata[q]['perr']): # Compute the transit model try: tmod = psm(time, 'binned') except Exception as e: if inp.debug: print("Transit optimization exception:", str(e)) return 1.e20 # Compute the PLD model pmod, ypld, yerr = PLDFlux(c, fpix, perr, tmod, crowding=crwd) # Compute the GP model try: gp.compute(time, yerr) except Exception as e: if inp.debug: print("Transit optimization exception:", str(e)) return 1.e20 ll += gp.lnlikelihood(ypld) if inp.debug: print("Likelihood: ", ll) return -ll # Run the optimizer. init = [info['RpRs'], info['b'], 0.25, 0.25] bounds = [[1.e-4, 0.5], [0., 0.95], [0., 1.], [0., 1.]] res = fmin_l_bfgs_b(negll, init, approx_grad=True, bounds=bounds) RpRs, bcirc, q1, q2 = res[0] # Save these! np.savez(os.path.join(inp.datadir, str(inp.id), '_data', 'rbqq.npz'), RpRs=RpRs, bcirc=bcirc, q1=q1, q2=q2) # Pre-compute the transit model psm = ps.Transit(per=per, q1=q1, q2=q2, RpRs=RpRs, rhos=rhos, tN=tN, ecw=0., esw=0., bcirc=bcirc, MpMs=0.) # Now, finally, compute the PLD flux and errors for q in inp.quarters: if len(pdata[q]['time']) == 0: continue # PLD and GP coeffs for this quarter c = pdata[q]['dvec'][iPLD:] x = pdata[q]['dvec'][:iPLD] # Crowding parameter crwd = pdata[q]['crwd'] # Reset tdata[q]['pmod'] = [] tdata[q]['yerr'] = [] tdata[q]['ypld'] = [] pdata[q]['pmod'] = [] pdata[q]['yerr'] = [] pdata[q]['ypld'] = [] # Loop over all transits for time, fpix, perr in zip(tdata[q]['time'], tdata[q]['fpix'], tdata[q]['perr']): # Compute the transit model if len(tN): tmod = psm(time, 'binned') else: tmod = 1. # Compute the PLD model pmod, ypld, yerr = PLDFlux(c, fpix, perr, tmod, crowding=crwd) # The pixel model tdata[q]['pmod'].append(pmod) # The errors on our PLD-detrended flux tdata[q]['yerr'].append(yerr) # The PLD-detrended, transitless flux # NOTE: This is just for verification purposes, since we used # a very quick transit optimization to compute this! tdata[q]['ypld'].append(ypld) # Now loop over all chunks in the full (processed) data for time, fpix, perr in zip(pdata[q]['time'], pdata[q]['fpix'], pdata[q]['perr']): # Compute the transit model if len(tN): tmod = psm(time, 'binned') else: tmod = 1. # Compute the PLD model pmod, ypld, yerr = PLDFlux(c, fpix, perr, tmod, crowding=crwd) # The pixel model pdata[q]['pmod'].append(pmod) # The errors on our PLD-detrended flux pdata[q]['yerr'].append(yerr) # The PLD-detrended, transitless flux # NOTE: This is just for verification purposes, since we used # a very quick transit optimization to compute this! pdata[q]['ypld'].append(ypld) np.savez_compressed(os.path.join(inp.datadir, str(inp.id), '_data', 'trn.npz'), data=tdata, hash=GitHash()) np.savez_compressed(os.path.join(inp.datadir, str(inp.id), '_data', 'prc.npz'), data=pdata, hash=GitHash()) return True
def PlotTransits(input_file = None, ax = None, clobber = False): ''' ''' # Try to use Agg for plotting pl.switch_backend('Agg') # Input file inp = Input(input_file) detpath = os.path.join(inp.datadir, str(inp.id), '_detrend') iPLD = len(inp.kernel.pars) # Have we done this already? if ax is None and not (inp.clobber or clobber): if os.path.exists(os.path.join(inp.datadir, str(inp.id), '_plots', 'folded.png')): return None, None if ax is None and not inp.quiet: print("Plotting transits...") # Load the info info = DownloadInfo(inp.id, inp.dataset, trninfo = inp.trninfo, inject = inp.inject, datadir = inp.datadir, clobber = False, ttvs = inp.ttvs, pad = inp.padbkg) tdur = info['tdur'] tN = info['tN'] per = info['per'] rhos = info['rhos'] # Are there any transits? if len(tN) == 0: return None, None if not (inp.clobber or clobber) and os.path.exists(os.path.join(inp.datadir, str(inp.id), '_data', 'fold.npz')): foo = np.load(os.path.join(inp.datadir, str(inp.id), '_data', 'fold.npz')) t = foo['t'] f = foo['f'] else: # Get our transit data t = np.array([], dtype = float) f = np.array([], dtype = float) tdata = GetData(inp.id, data_type = 'trn', datadir = inp.datadir) # Transit model try: foo = np.load(os.path.join(inp.datadir, str(inp.id), '_data', 'rbqq.npz')) RpRs = foo['RpRs'] bcirc = foo['bcirc'] q1 = foo['q1'] q2 = foo['q2'] except FileNotFoundError: raise FileNotFoundError("Unable to load transit parameters.") psm = ps.Transit(per = per, q1 = q1, q2 = q2, RpRs = RpRs, rhos = rhos, tN = tN, ecw = 0., esw = 0., bcirc = bcirc, MpMs = 0.) # Loop over all quarters for q in inp.quarters: # Empty? if len(tdata[q]['time']) == 0: continue # Info for this quarter crwd = tdata[q]['crwd'] c = tdata[q]['dvec'][iPLD:] x = tdata[q]['dvec'][:iPLD] inp.kernel.pars = x gp = george.GP(inp.kernel) # Loop over all transits for time, fpix, perr in zip(tdata[q]['time'], tdata[q]['fpix'], tdata[q]['perr']): # Compute the transit model tmod = psm(time, 'binned') # Compute the PLD model pmod, ypld, yerr = PLDFlux(c, fpix, perr, tmod, crowding = crwd) # Compute the GP model gp.compute(time, yerr) mu, _ = gp.predict(ypld, time) t = np.append(t, time) # TODO: Verify that this is in fact the correct way # to whiten the transit! f = np.append(f, np.sum(fpix, axis = 1) / (mu + pmod)) # Fold the data t -= np.array([tN[np.argmin(np.abs(tN - ti))] for ti in t]) # Save it! np.savez(os.path.join(inp.datadir, str(inp.id), '_data', 'fold.npz'), t = t, f = f) # Plot if ax is None: userax = False fig, ax = pl.subplots(1, 1, figsize = (14, 6)) else: userax = True xlim = (-inp.padtrn * tdur / 2., inp.padtrn * tdur / 2.) fvis = f[np.where((t > xlim[0]) & (t < xlim[1]))] minf = np.min(fvis) maxf = np.max(fvis) padf = 0.1 * (maxf - minf) ylim = (minf - padf, maxf + padf) ax.plot(t, f, 'k.', alpha = min(1.0, max(0.05, 375. / len(fvis)))) # Bin to median bins = np.linspace(xlim[0], xlim[1], inp.tbins) delta = bins[1] - bins[0] idx = np.digitize(t, bins) med = [np.median(f[idx == k]) for k in range(inp.tbins)] ax.plot(bins - delta / 2., med, 'ro', alpha = 0.75) # Is this a fake transit injection? If so, plot the true model if inp.inject is not None and inp.inject != {}: pskwargs = dict(inp.inject) pskwargs.pop('tN', None) pskwargs.update({'t0': 0.}) psm = ps.Transit(**pskwargs) t0 = np.linspace(-inp.padtrn * tdur / 2., inp.padtrn * tdur / 2., 1000) f0 = psm(t0, 'binned') ax.plot(t0, f0, 'r--') ax.set_xlim(xlim) ax.set_ylim(ylim) if not userax: ax.set_title('Folded Whitened Transits', fontsize = 24) ax.set_xlabel('Time (days)', fontsize = 22) ax.set_ylabel('Flux', fontsize = 22) if not userax: fig.savefig(os.path.join(inp.datadir, str(inp.id), '_plots', 'folded.png'), bbox_inches = 'tight') return fig, ax else: return ax
# Get the raw pixel flux calc = False ncad = 3000 time = np.linspace(0, 13.7 * 2, ncad) if calc: fpix = GenerateData(ncad=ncad) np.savez('fpix.npz', fpix=fpix) else: fpix = np.load('fpix.npz')['fpix'] fpix[np.where(np.isnan(fpix))] = 0. flux_notrn = np.sum(fpix, axis=(1, 2)) # Add a transit model trn = ps.Transit(t0=7., RpRs=0.03, per=12, rhos=0.01) transit = trn(time) fpix *= transit.reshape(-1, 1, 1) # Get the SAP flux flux = np.sum(fpix, axis=(1, 2)) med = np.nanmedian(flux) # Regress with 2nd order PLD. Note that # we are cheating a bit since I'm regressing # on the flux with *no transit* model. # In reality, we would *jointly fit* # the systematics and the transit, but # this is a shortcut. D = fpix.reshape(ncad, -1) / flux.reshape(-1, 1) D = np.hstack((D, D * D))
def plot_random_planets(self, ncurves=50, over=0.1, pmin=None, pmax=None, show_vsys=False, show_trend=False): """ Display the RV data together with curves from the posterior predictive. A total of `ncurves` random samples are chosen, and the Keplerian curves are calculated covering 100 + `over`% of the timespan of the data. """ samples = self.get_sorted_planet_samples() if self.max_components > 0: samples, mask = \ self.apply_cuts_period(samples, pmin, pmax, return_mask=True) else: mask = np.ones(samples.shape[0], dtype=bool) t = self.data[:,0].copy() tt = np.linspace(t[0]-over*t.ptp(), t[-1]+over*t.ptp(), 10000+int(100*over)) y = self.data[:,1].copy() yerr = self.data[:,2].copy() # select random `ncurves` indices # from the (sorted, period-cut) posterior samples ii = np.random.randint(samples.shape[0], size=ncurves) _, ax = plt.subplots(1,1) ## plot the Keplerian curves for i in ii: f = np.zeros_like(tt) pars = samples[i, :].copy() nplanets = pars.size / self.n_dimensions for j in range(int(nplanets)): P = pars[j + 0*self.max_components] if P==0.0: continue RpRs = pars[j + 1*self.max_components] aRs = pars[j + 2*self.max_components] phi = pars[j + 3*self.max_components] Tc = t[0] + (P*phi) try: f += ps.Transit(per=P, aRs=aRs, RpRs=RpRs, t0=Tc)(tt) - 1.0 except: print('Failed for:', P, RpRs, aRs, Tc) # v += keplerian(tt, P, K, ecc, w, t0, 0.) f0 = self.posterior_sample[mask][i, -1] f += f0 ax.plot(tt, f, alpha=0.2, color='k') # if show_vsys: # ax.plot(t, vsys*np.ones_like(t), alpha=0.2, color='r', ls='--') ## plot the data ax.errorbar(t, y, yerr, fmt='.') ax.set(xlabel='Time [days]', ylabel='Flux []') plt.tight_layout()
def DownloadKeplerData( id, datadir='', long_cadence=True, clobber=False, bad_bits=[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17], aperture='optimal', quarters=range(18), quiet=False, inject={}, ttvs=False, pad=2.0, trninfo={}, **kwargs): ''' ''' if not clobber: try: # For some reason, numpy saves it as a 0-d array. # See http://stackoverflow.com/questions/8361561/recover-dict-from-0-d-numpy-array data = np.load(os.path.join(datadir, str(id), '_data', 'raw.npz'))['data'][()] if not quiet: print("Loading saved TPF data...") return data except FileNotFoundError: pass # Create directories for sd in ['_data', '_plots', '_detrend']: if not os.path.exists(os.path.join(datadir, str(id), sd)): os.makedirs(os.path.join(datadir, str(id), sd)) if not quiet: print("Downloading TPF data...") # Download the data try: obj = get_kplr_obj(id) except: raise Exception("Unable to access online database.") data = EmptyData(quarters) # Target pixel files tpf = obj.get_target_pixel_files(short_cadence=not long_cadence) # Lightcurves lcs = obj.get_light_curves(short_cadence=not long_cadence) nfiles = len(tpf) if nfiles == 0: raise Exception("No pixel files for selected object!") elif len(lcs) != nfiles: # This shouldn't happen. If it does, I'll need to re-code this section raise Exception( "Mismatch in number of lightcurves and number of pixel files.") # Loop over the target pixel files/lightcurve files for fnum in range(nfiles): with tpf[fnum].open(clobber=clobber) as f: ap = f[2].data qdata = f[1].data quarter = f[0].header['QUARTER'] if quarter not in quarters: continue crwd = f[1].header['CROWDSAP'] with lcs[fnum].open(clobber=clobber) as f: hdu_data = f[1].data pdcf = np.array(hdu_data["pdcsap_flux"] - np.nanmedian(hdu_data["pdcsap_flux"])) pdce = np.array(hdu_data["pdcsap_flux_err"]) if aperture == 'optimal': idx = np.where(ap & 2) elif aperture == 'full': idx = np.where(ap & 1) else: raise Exception('ERROR: Invalid aperture setting `%s`.' % aperture) time = np.array(qdata.field('TIME'), dtype='float64') if len(time) != len(pdcf): # This shouldn't happen. If it does, I'll need to re-code this section raise Exception('Mismatch in length of pixel flux and PDC flux!') # Any NaNs will screw up the transit model calculation, # so we need to remove them now. nan_inds = list(np.where(np.isnan(time))[0]) time = np.delete(time, nan_inds) cadn = np.array(qdata.field('CADENCENO'), dtype='int32') cadn = np.delete(cadn, nan_inds) flux = np.array(qdata.field('FLUX'), dtype='float64') flux = np.delete(flux, nan_inds, 0) fpix = np.array([f[idx] for f in flux], dtype='float64') # Inject transits? if (inject is not None) and (inject != {}) and (inject != False): psm = ps.Transit(**inject) tmod = (psm(time, 'binned') - 1.) * crwd + 1. for n in range(fpix.shape[1]): fpix[:, n] *= tmod perr = np.array([f[idx] for f in qdata.field('FLUX_ERR')], dtype='float64') perr = np.delete(perr, nan_inds, 0) pdcf = np.delete(pdcf, nan_inds, 0) pdce = np.delete(pdce, nan_inds, 0) # Remove bad bits quality = qdata.field('QUALITY') quality = np.delete(quality, nan_inds) qual_inds = [] for b in bad_bits: qual_inds += list(np.where(quality & 2**(b - 1))[0]) nan_inds = list(np.where(np.isnan(np.sum(fpix, axis=1)))[0]) bad_inds = np.array(sorted(list(set(qual_inds + nan_inds)))) time = np.delete(time, bad_inds) cadn = np.delete(cadn, bad_inds) fpix = np.delete(fpix, bad_inds, 0) perr = np.delete(perr, bad_inds, 0) pdcf = np.delete(pdcf, bad_inds, 0) pdce = np.delete(pdce, bad_inds, 0) data[quarter].update({ 'time': time, 'fpix': fpix, 'perr': perr, 'cadn': cadn, 'crwd': crwd, 'pdcf': pdcf, 'pdce': pdce }) # Save to disk np.savez_compressed(os.path.join(datadir, str(id), '_data', 'raw.npz'), data=data, hash=GitHash()) # Download the info so we have it saved on disk DownloadKeplerInfo(id, datadir=datadir, clobber=clobber, ttvs=ttvs, pad=pad, inject=inject, trninfo=trninfo) return data
fig, ax = pl.subplots(5, 5, figsize=(24, 16)) fig.subplots_adjust(wspace=0, hspace=0, left=0.01, right=0.99, bottom=0.01, top=0.99) ax = ax.flatten() for axis in ax: axis.xaxis.set_ticklabels([]) axis.yaxis.set_ticklabels([]) # Different radii time = np.linspace(-0.5, 0.5, 1000) for RpRs in [0.1, 0.09, 0.08]: trn = ps.Transit(RpRs=RpRs) ax[0].plot(time, trn(time), label='RpRs = %.2f' % RpRs) ax[0].legend(loc='lower left', fontsize=8) ax[0].margins(0., 0.2) ax[0].annotate('DIFFERENT RADII', xy=(0.05, 0.9), xycoords='axes fraction') # Different periods time = np.linspace(-0.5, 0.5, 1000) for per in [10., 20., 30.]: trn = ps.Transit(per=per) ax[1].plot(time, trn(time), label='per = %.0f' % per) ax[1].legend(loc='lower left', fontsize=8) ax[1].margins(0., 0.2) ax[1].annotate('DIFFERENT PERIODS', xy=(0.05, 0.9), xycoords='axes fraction')
def Depth(RpRs, **kwargs): return 1 - ps.Transit(RpRs=RpRs, **kwargs)([kwargs.get('t0', 0.)])
kwargs = dict(RpRs = 0.1, b = 0.5, ecc = 0.5, w = 0., MpMs = 0., rhos = 1.4, per = 5., t0 = 0., q1 = 0.45, q2 = 0.3, maxpts = 20000, exptime = ps.KEPLONGEXP) # Set up the plot fig, ax = pl.subplots(2, 2, figsize = (10, 8)) fig.subplots_adjust(hspace = 0.35) fig.suptitle('Pysyzygy Transit Model', fontsize = 18) ax = ax.flatten() # The transit lightcurve trn = ps.Transit(**kwargs) time = np.linspace(-0.15, 0.15, 1000) ax[0].plot(time, trn(time, 'binned'), 'b-', label = 'binned') ax[0].plot(time, trn(time, 'unbinned'), 'r-', label = 'unbinned') ax[0].margins(0., 0.25) ax[0].legend(loc = 'lower left', fontsize = 8) ax[0].set_xlabel('Time (days)', fontsize = 12) ax[0].set_ylabel('Normalized flux', fontsize = 12) ax[0].set_title('Transit lightcurve') # The orbit trn = ps.Transit(fullorbit = True, **kwargs) time = np.linspace(-5, 5, 1000) ax[1].plot(trn(time, 'x'), trn(time, 'y'), 'k-')