def get_downsampled_mcl(self, mcl_fractions): # Returns the mean camber line in downsampled form mcl = self.mcl_coordinates # Find distances along mcl, assuming linear interpolation mcl_distances_between_points = np.sqrt( np.power(mcl[:-1, 0] - mcl[1:, 0], 2) + np.power(mcl[:-1, 1] - mcl[1:, 1], 2) ) mcl_distances_cumulative = np.hstack((0, np.cumsum(mcl_distances_between_points))) mcl_distances_cumulative_normalized = mcl_distances_cumulative / mcl_distances_cumulative[-1] mcl_downsampled_x=np.interp( x=mcl_fractions, xp=mcl_distances_cumulative_normalized, fp=mcl[:,0] ) mcl_downsampled_y = np.interp( x=mcl_fractions, xp=mcl_distances_cumulative_normalized, fp=mcl[:, 1] ) mcl_downsampled = np.column_stack((mcl_downsampled_x, mcl_downsampled_y)) return mcl_downsampled
def infer(hp_data_list, xplot, ab=0.002, num_ite=1000, burn_in=500, run_time=np.pi): # GP kernel covfn = cov.GGCPCov(name='cosine', a=ab, b=ab, nterms=32, m=2, d=1) Omega = np.diag(1 / covfn.lambdas) # plotted x phixstar = covfn._phi(xplot) # sensors recording samples rm_f = ElementWiseRunningStatistic(RunningMean) rstd_f = ElementWiseRunningStatistic(RunningSTD) rm_mu = ElementWiseRunningStatistic(RunningMean) rstd_mu = ElementWiseRunningStatistic(RunningSTD) phi_f = lambda x: np.exp(-0.8 * x) mu_constant = 50 # gibbs ierations e1, e2 = 1 + 1 / run_time, 1 / (run_time + 1) hist_cluster0 = [[[], hp_data[0, -1]] for hp_data in hp_data_list] num_hist = len(hp_data_list) for ite in range(1, num_ite + 1): K = 0 hist_cluster = hist_cluster0.copy() # sampling branching structures and decomposing HP's to clusters of PP's for hp_data in hp_data_list: Sij = sij(hp_data, phi_f, mu_constant) hist_cluster += [[(hp_data[0,i+1:])[Sij[i+1:]==i+1]-hp_data[0,i],hp_data[0,i]] \ if any(Sij[i+1:]==i+1)>0 else [[], hp_data[0,i]] for i in range(hp_data.shape[1]-1)] K += sum(Sij == 0) # triggering kernel w, Q, _ = laplace_map_w(covfn, hist_cluster, Omega) mu_f, sigma_f, Sigma_f = laplace_pred_w(phixstar, w, Q) samples = np.random.multivariate_normal(mu_f, Sigma_f, 1) # background intensity K /= num_hist mu_constant = np.random.gamma(K * e1, e2) # recording samples if ite > burn_in: rm_f.observe(samples) rstd_f.observe(samples) tmp_mu_array = np.array([[mu_constant]]) rm_mu.observe(tmp_mu_array) rstd_mu.observe(tmp_mu_array) # updating the triggering kernel samples = 0.5 * samples**2 phi_f = lambda x: np.interp(x, xplot[0], samples[0]) return rm_f, rstd_f, rm_mu, rstd_mu
def resample_rest_frame(spectra, spectra_ivar, zs, lam_obs, lam0): """ Resamples spectra with known red-shifts into rest frame at samples given by lam0. """ if lam_obs.ndim == 1: lam_obs = np.tile(lam_obs, (spectra.shape[0], 1)) lam_mat = np.zeros(spectra.shape) spectra_resampled = np.zeros((spectra.shape[0], len(lam0))) spectra_ivar_resampled = np.zeros((spectra.shape[0], len(lam0))) for i in range(spectra.shape[0]): lam_mat[i, :] = lam_obs[i, :] / (1 + zs[i]) spectra_resampled[i, :] = np.interp(x=lam0, xp=lam_mat[i, :], fp=spectra[i, :], left=np.nan, right=np.nan) # resample variances linearly, not inverse variances spec_var = 1. / spectra_ivar[i, :] spectra_ivar_resampled[i, :] = 1. / np.interp( x=lam0, xp=lam_mat[i, :], fp=spec_var, left=np.nan, right=np.nan) return spectra_resampled, spectra_ivar_resampled, lam_mat
def interpolate_data(data, mask): """ Interpolate over missing entries """ assert data.shape == mask.shape and mask.dtype == bool T, N = data.shape interp_data = data.copy() if np.any(~mask): for n in range(N): if np.sum(mask[:,n]) >= 2: t_missing = np.arange(T)[~mask[:,n]] t_given = np.arange(T)[mask[:,n]] y_given = data[mask[:,n], n] interp_data[~mask[:,n], n] = np.interp(t_missing, t_given, y_given) else: # Can't do much if we don't see anything... just set it to zero interp_data[~mask[:,n], n] = 0 return interp_data
def project_to_bands(spectra, wavelengths): fluxes = np.zeros(5) for i, band in enumerate(['u', 'g', 'r', 'i', 'z']): # interpolate sensitivity curve onto wavelengths sensitivity = np.interp(wavelengths, planck.wavelength_lookup[band] * (10**10), planck.sensitivity_lookup[band]) norm = np.sum(sensitivity) if norm <= 0.: fluxes[i] = 0. # catch zero case else: # conversion flambda2fnu = wavelengths**2 / 2.99792e18 fthru = np.sum(sensitivity * spectra * flambda2fnu) / norm #mags = -2.5 * np.log10(fthru) - (48.6 - 2.5*17) #fluxes[i] = np.power(10., (mags - 22.5)/-2.5) # We don't have to log and exponentiate fluxes[i] = fthru * flux_constant if np.isnan(fluxes[i]): print "project_to_bands isnan in band %s" % band, fthru, flux_constant, norm return fluxes
def interpolate_basis(self, basis, dt, dt_max, norm=True): # Interpolate basis at the resolution of the data L, B = basis.shape t_int = np.arange(0.0, dt_max, step=dt) t_bas = np.linspace(0.0, dt_max, L) ibasis = np.zeros((len(t_int), B)) for b in np.arange(B): ibasis[:, b] = np.interp(t_int, t_bas, basis[:, b]) # Normalize so that the interpolated basis has volume 1 if norm: # ibasis /= np.trapz(ibasis,t_int,axis=0) ibasis /= (dt * np.sum(ibasis, axis=0)) if not self.allow_instantaneous: # Typically, the impulse responses are applied to times # (t+1:t+R). That means we need to prepend a row of zeros to make # sure the basis remains causal ibasis = np.vstack((np.zeros((1, B)), ibasis)) return ibasis
def interpolate_basis(self, basis, dt, dt_max, norm=True): # Interpolate basis at the resolution of the data L,B = basis.shape t_int = np.arange(0.0, dt_max, step=dt) t_bas = np.linspace(0.0, dt_max, L) ibasis = np.zeros((len(t_int), B)) for b in np.arange(B): ibasis[:,b] = np.interp(t_int, t_bas, basis[:,b]) # Normalize so that the interpolated basis has volume 1 if norm: # ibasis /= np.trapz(ibasis,t_int,axis=0) ibasis /= (dt * np.sum(ibasis, axis=0)) if not self.allow_instantaneous: # Typically, the impulse responses are applied to times # (t+1:t+R). That means we need to prepend a row of zeros to make # sure the basis remains causal ibasis = np.vstack((np.zeros((1,B)), ibasis)) return ibasis
def binRunning(runSig, runTime, binwidth=0.001): return np.interp(np.arange(0, b.lastBehaviorTime, binwidth), runTime, runSig)[:-1]
def fit_weights_given_basis(B, lam0, X, inv_var, z_n, lam_obs, return_loss=False, sgd_iter=100): """ Weighted optimization routine to fit the values of \log w given basis B. """ #convert spec_n to lam0 spec_n_resampled = np.interp(lam0, lam_obs / (1 + z_n), X, left=np.nan, right=np.nan) ivar_n_resampled = np.interp(lam0, lam_obs / (1 + z_n), inv_var, left=np.nan, right=np.nan) spec_n_resampled[np.isnan(spec_n_resampled)] = 0.0 ivar_n_resampled[np.isnan(ivar_n_resampled)] = 0.0 def loss_omegas(omegas): """ loss over weights with respect to fixed basis """ ll_omega = .5 / (100.) * np.sum(np.square(omegas)) Xtilde = np.dot(np.exp(omegas), B) return np.sum( ivar_n_resampled * np.square(spec_n_resampled - Xtilde)) + ll_omega loss_omegas_grad = grad(loss_omegas) # first wail on it with gradient descent/momentum omegas = .01 * np.random.randn(B.shape[0]) momentum = .9 learning_rate = 1e-4 cur_dir = np.zeros(omegas.shape) lls = np.zeros(sgd_iter) for epoch in range(sgd_iter): grad_th = loss_omegas_grad(omegas) cur_dir = momentum * cur_dir + (1.0 - momentum) * grad_th omegas -= learning_rate * cur_dir lls[epoch] = loss_omegas(omegas) step_mag = np.sqrt(np.sum(np.square(learning_rate * cur_dir))) if epoch % 20 == 0: print "{0:15}|{1:15}|{2:15}".format(epoch, "%7g" % lls[epoch], "%2.4f" % step_mag) # tighten it up w/ LBFGS res = minimize(x0=omegas, fun=loss_omegas, jac=loss_omegas_grad, method='L-BFGS-B', options={ 'disp': True, 'maxiter': 10000 }) # return the loss function handle as well - for debugging if return_loss: return np.exp(res.x), loss_omegas return np.exp(res.x)
def procFlares(prefix, filenames, path, clobberGP=False, makePlots=False, writeLog=True): if makePlots: plots_path = path + 'plots/' if not os.path.exists(plots_path): os.makedirs(plots_path) gp_path = path + 'gp/' #if not os.path.exists(gp_path): #os.makedirs(gp_path) log_path = path + 'log/' #if not os.path.exists(log_path): #os.makedirs(log_path) if writeLog: if os.path.exists(log_path + prefix + '.log'): os.remove(log_path + prefix + '.log') # Columns for flare table FL_files = np.array([]) FL_TICs = np.array([]) FL_id = np.array([]) FL_t0 = np.array([]) FL_t1 = np.array([]) FL_f0 = np.array([]) FL_f1 = np.array([]) FL_ed = np.array([]) FL_ed_err = np.array([]) FL_skew = np.array([]) FL_cover = np.array([]) FL_mu = np.array([]) FL_std = np.array([]) FL_g_amp = np.array([]) FL_mu_err = np.array([]) FL_std_err = np.array([]) FL_g_amp_err = np.array([]) FL_tpeak = np.array([]) FL_fwhm = np.array([]) FL_f_amp = np.array([]) FL_tpeak_err = np.array([]) FL_fwhm_err = np.array([]) FL_f_amp_err = np.array([]) FL_g_chisq = np.array([]) FL_f_chisq = np.array([]) FL_g_fwhm_win = np.array([]) FL_f_fwhm_win = np.array([]) # Columns for param table P_median = np.array([]) P_s_window = np.array([]) P_acf_1dt = np.array([]) P_acf_amp = np.array([]) failed_files = [] for k in range(len(filenames)): start_time = timing.time() filename = filenames[k] TIC = int(filename.split('-')[-3]) file = path + filename if makePlots: fig, axes = plt.subplots(figsize=(16, 16), nrows=4, sharex=True) print('Processing ' + filename) gp_data_file = gp_path + filename + '.gp' gp_param_file = gp_path + filename + '.gp.par' median = -1 s_window = -1 acf_1dt = -1 acf_amp = -1 with fits.open(file, mode='readonly') as hdulist: try: tess_bjd = hdulist[1].data['TIME'] quality = hdulist[1].data['QUALITY'] pdcsap_flux = hdulist[1].data['PDCSAP_FLUX'] pdcsap_flux_error = hdulist[1].data['PDCSAP_FLUX_ERR'] except: P_median = np.append(P_median, median) P_s_window = np.append(P_s_window, s_window) P_acf_1dt = np.append(P_acf_1dt, acf_1dt) P_acf_amp = np.append(P_acf_amp, acf_amp) failed_files.append(filename) np.savetxt(gp_data_file, ([])) print('Reading file ' + filename + ' failed') continue if makePlots: axes[0].plot(tess_bjd, pdcsap_flux) # Cut out poor quality points ok_cut = (quality == 0) & (~np.isnan(tess_bjd)) & (~np.isnan(pdcsap_flux))\ & (~np.isnan(pdcsap_flux_error)) tbl = Table([tess_bjd[ok_cut], pdcsap_flux[ok_cut], \ pdcsap_flux_error[ok_cut]], names=('TIME', 'PDCSAP_FLUX', 'PDCSAP_FLUX_ERR')) df_tbl = tbl.to_pandas() median = np.nanmedian(df_tbl['PDCSAP_FLUX']) # Estimate the period of the LC with autocorrelation acf = fh.autocorr_estimator(tbl['TIME'], tbl['PDCSAP_FLUX']/median, \ yerr=tbl['PDCSAP_FLUX_ERR']/median, min_period=0.1, max_period=27, max_peaks=2) if len(acf['peaks']) > 0: acf_1dt = acf['peaks'][0]['period'] acf_amp = acf['autocorr'][1][np.where( acf['autocorr'][0] == acf_1dt)] mask = np.where( (acf['autocorr'][0] == acf['peaks'][0]['period']))[0] acf_1pk = acf['autocorr'][1][mask][0] s_window = int(acf_1dt / np.fabs(np.nanmedian(np.diff(df_tbl['TIME']))) / 6) else: acf_1dt = (tbl['TIME'][-1] - tbl['TIME'][0]) / 2 acf_amp = 0 s_window = 128 P_median = np.append(P_median, median) P_s_window = np.append(P_s_window, s_window) P_acf_1dt = np.append(P_acf_1dt, acf_1dt) P_acf_amp = np.append(P_acf_amp, acf_amp) # Run GP fit on the lightcurve if we haven't already if os.path.exists(gp_data_file) and not clobberGP: # Failed GP regression will produce an empty file if os.path.getsize(gp_data_file) == 0: print(file + ' failed (previously) during GP regression') failed_files.append(filename) continue print('GP file already exists, loading...') times, smo, var = np.loadtxt(gp_data_file) else: smo = np.zeros(len(df_tbl['TIME'])) try: if makePlots: ax = axes[1] else: ax = None times, smo, var, params = iterGP_rotation(df_tbl['TIME'].values, df_tbl['PDCSAP_FLUX'].values/median, \ df_tbl['PDCSAP_FLUX_ERR'].values/median, acf_1dt, acf_1pk, ax=ax) #np.savetxt(gp_param_file, params['logs2'], params['logamp'], params['logperiod'], \ # params['logq0'], params['logdeltaq'], params['mix'], params['period']) np.savetxt(gp_param_file, params) np.savetxt(gp_data_file, (times, smo, var)) except: traceback.print_exc() failed_files.append(filename) np.savetxt(gp_data_file, ([])) print(filename + ' failed during GP fitting') continue # The GP is produced from a downsampled lightcurve. Need to interpolate to # compare GP and full LC smo_int = np.interp(tbl['TIME'], times, smo) # Search for flares in the smoothed lightcurve x = np.array(tbl['TIME']) y = np.array(tbl['PDCSAP_FLUX'] / median - smo_int) yerr = np.array(tbl['PDCSAP_FLUX_ERR'] / median) FL = fh.FINDflare(y, yerr, avg_std=True, std_window=s_window, N1=3, N2=1, N3=3) if makePlots: axes[3].plot(x, y, zorder=1) for j in range(len(FL[0])): s1, s2 = FL[0][j], FL[1][j] + 1 axes[3].scatter(x[s1:s2], y[s1:s2], zorder=2) # Measure properties of detected flares if makePlots: fig_fl, axes_fl = plt.subplots(figsize=(16, 16), nrows=4, ncols=4) for j in range(len(FL[0])): s1, s2 = FL[0][j], FL[1][j] + 1 tstart, tstop = x[s1], x[s2] dx_fac = 10 dx = tstop - tstart x1 = tstart - dx * dx_fac / 2 x2 = tstop + dx * dx_fac / 2 mask = (x > x1) & (x < x2) # Mask out other flare detections when fitting models other_mask = np.ones(len(x), dtype=bool) for i in range(len(FL[0])): s1other, s2other = FL[0][i], FL[1][i] + 1 if i == j: continue other_mask[s1other:s2other] = 0 popt1, pstd1, g_chisq, popt2, pstd2, f_chisq, skew, cover = \ fitFlare(x[other_mask], y[other_mask], yerr[other_mask], x1, x2) mu, std, g_amp = popt1[0], popt1[1], popt1[2] mu_err, std_err, g_amp_err = pstd1[0], pstd1[1], pstd1[2] tpeak, fwhm, f_amp = popt2[0], popt2[1], popt2[2] tpeak_err, fwhm_err, f_amp_err = pstd2[0], pstd2[1], pstd2[2] f_fwhm_win = fwhm / (x2 - x1) g_fwhm_win = std / (x2 - x1) ed, ed_err = measureED(x, y, yerr, tpeak, fwhm) FL_files = np.append(FL_files, filename) FL_TICs = np.append(FL_TICs, TIC) FL_t0 = np.append(FL_t0, x1) FL_t1 = np.append(FL_t1, x2) FL_f0 = np.append(FL_f0, np.nanmedian(tbl['PDCSAP_FLUX'][s1:s2])) FL_f1 = np.append(FL_f1, np.nanmax(tbl['PDCSAP_FLUX'][s1:s2])) FL_ed = np.append(FL_ed, ed) FL_ed_err = np.append(FL_ed_err, ed_err) FL_skew = np.append(FL_skew, skew) FL_cover = np.append(FL_cover, cover) FL_mu = np.append(FL_mu, mu) FL_std = np.append(FL_std, std) FL_g_amp = np.append(FL_g_amp, g_amp) FL_mu_err = np.append(FL_mu_err, mu_err) FL_std_err = np.append(FL_std_err, std_err) FL_g_amp_err = np.append(FL_g_amp_err, g_amp_err) FL_tpeak = np.append(FL_tpeak, tpeak) FL_fwhm = np.append(FL_fwhm, fwhm) FL_f_amp = np.append(FL_f_amp, f_amp) FL_tpeak_err = np.append(FL_tpeak_err, tpeak_err) FL_fwhm_err = np.append(FL_fwhm_err, fwhm_err) FL_f_amp_err = np.append(FL_f_amp_err, f_amp_err) FL_g_chisq = np.append(FL_g_chisq, g_chisq) FL_f_chisq = np.append(FL_f_chisq, f_chisq) FL_g_fwhm_win = np.append(FL_g_fwhm_win, g_fwhm_win) FL_f_fwhm_win = np.append(FL_f_fwhm_win, f_fwhm_win) if makePlots and j < 15: row_idx = j // 4 col_idx = j % 4 axes_fl[row_idx][col_idx].errorbar(x[mask], y[mask], yerr=yerr[mask]) axes_fl[row_idx][col_idx].scatter(x[s1:s2], y[s1:s2]) if popt1[0] > 0: xmodel = np.linspace(x1, x2) ymodel = fh.aflare1(xmodel, tpeak, fwhm, f_amp) axes_fl[row_idx][col_idx].plot(xmodel, ymodel, label=r'$\chi_{f}$ = ' + '{:.3f}'.format(f_chisq) \ + '\n FWHM/window = ' + '{:.2f}'.format(f_fwhm_win)) ymodel = fh.gaussian(xmodel, mu, std, g_amp) axes_fl[row_idx][col_idx].plot(xmodel, ymodel, label=r'$\chi_{g}$ = ' + '{:.3f}'.format(g_chisq) \ + '\n FWHM/window = ' + '{:.2f}'.format(g_fwhm_win)) axes_fl[row_idx][col_idx].axvline(tpeak - fwhm / 2, linestyle='--') axes_fl[row_idx][col_idx].axvline(tpeak + fwhm / 2, linestyle='--') axes_fl[row_idx][col_idx].legend() axes_fl[row_idx][col_idx].set_title('Skew = ' + '{:.3f}'.format(skew)) if makePlots: fig.suptitle(filename) axes[0].set_xlabel('Time [BJD - 2457000, days]') axes[0].set_ylabel('Flux [e-/s]') axes[1].set_xlabel('Time [BJD - 2457000, days]') axes[1].set_ylabel('Normalized Flux') axes[2].set_xlabel('Time [BJD - 2457000, days]') axes[2].set_ylabel('Rolling STD of GP') axes[3].set_xlabel('Time [BJD - 2457000, days]') axes[3].set_ylabel('Normalized Flux - GP') fig.savefig(plots_path + filename + '.png', format='png') if len(FL[0] > 0): fig_fl.suptitle(filename) fig_fl.savefig(plots_path + filename + '_flares.png', format='png') plt.clf() if writeLog: with open(log_path + prefix + '.log', 'a') as f: time_elapsed = timing.time() - start_time num_flares = len(FL[0]) f.write('{:^15}'.format(str(k+1) + '/' + str(len(filenames))) + \ '{:<60}'.format(filename) + '{:<20}'.format(time_elapsed) + \ '{:<10}'.format(num_flares) + '\n') # Periodically write to the flare table file and param table file l = k + 1 ALL_TIC = pd.Series(filenames).str.split( '-', expand=True).iloc[:, -3].astype('int') ALL_FILES = pd.Series(filenames).str.split('/', expand=True).iloc[:, -1] flare_out = pd.DataFrame(data={'file':FL_files,'TIC':FL_TICs, 't0':FL_t0, 't1':FL_t1, \ 'med_flux':FL_f0, 'peak_flux':FL_f1, 'ed':FL_ed, \ 'ed_err':FL_ed_err, 'skew':FL_skew, 'cover':FL_cover, \ 'mu':FL_mu, 'std':FL_std, 'g_amp': FL_g_amp, 'mu_err':FL_mu_err, \ 'std_err':FL_std_err, 'g_amp_err':FL_g_amp_err,'tpeak':FL_tpeak, \ 'fwhm':FL_fwhm, 'f_amp':FL_f_amp, 'tpeak_err':FL_tpeak_err, \ 'fwhm_err':FL_fwhm_err, 'f_amp_err':FL_f_amp_err,'f_chisq':FL_f_chisq, \ 'g_chisq':FL_g_chisq, 'f_fwhm_win':FL_f_fwhm_win, 'g_fwhm_win':FL_g_fwhm_win}) flare_out.to_csv(log_path + prefix + '_flare_out.csv', index=False) param_out = pd.DataFrame(data={'file':ALL_FILES[:l], 'TIC':ALL_TIC[:l], 'med':P_median[:l], \ 's_window':P_s_window[:l], 'acf_1dt':P_acf_1dt[:l], 'acf_amp':P_acf_amp[:l]}) param_out.to_csv(log_path + prefix + '_param_out.csv', index=False) for k in range(len(failed_files)): print(failed_files[k])
def autocorr_estimator( x, y, yerr=None, min_period=None, max_period=None, oversample=2.0, smooth=2.0, max_peaks=10, ): """Estimate the period of a time series using the autocorrelation function .. note:: The signal is interpolated onto a uniform grid in time so that the autocorrelation function can be computed. Args: x (ndarray[N]): The times of the observations y (ndarray[N]): The observations at times ``x`` yerr (Optional[ndarray[N]]): The uncertainties on ``y`` min_period (Optional[float]): The minimum period to consider max_period (Optional[float]): The maximum period to consider oversample (Optional[float]): When interpolating, oversample the times by this factor (default: 2.0) smooth (Optional[float]): Smooth the autocorrelation function by this factor times the minimum period (default: 2.0) max_peaks (Optional[int]): The maximum number of peaks to identify in the autocorrelation function (default: 10) Returns: A dictionary with the computed autocorrelation function and the estimated period. For compatibility with the :func:`lomb_scargle_estimator`, the period is returned as a list with the key ``peaks``. """ if gaussian_filter is None: raise ImportError("scipy is required to use the autocorr estimator") if min_period is None: min_period = np.min(np.diff(x)) if max_period is None: max_period = x.max() - x.min() # Interpolate onto an evenly spaced grid dx = np.min(np.diff(x)) / float(oversample) xx = np.arange(x.min(), x.max(), dx) yy = np.interp(xx, x, y) # Estimate the autocorrelation function tau = xx - x[0] acor = autocorr_function(yy) smooth = smooth * min_period acor = gaussian_filter(acor, smooth / dx) # Find the peaks peak_inds = (acor[1:-1] > acor[:-2]) & (acor[1:-1] > acor[2:]) peak_inds = np.arange(1, len(acor) - 1)[peak_inds] peak_inds = peak_inds[tau[peak_inds] >= min_period] result = dict(autocorr=(tau, acor), peaks=[]) # No peaks were found if len(peak_inds) == 0 or tau[peak_inds[0]] > max_period: return result # Only one peak was found if len(peak_inds) == 1: result["peaks"] = [ dict(period=tau[peak_inds[0]], period_uncert=np.nan) ] return result # Check to see if second peak is higher if acor[peak_inds[1]] > acor[peak_inds[0]]: peak_inds = peak_inds[1:] # The first peak is larger than the maximum period if tau[peak_inds[0]] > max_period: return result result["peaks"] = [dict(period=tau[peak_inds[0]], period_uncert=np.nan)] return result