Exemple #1
0
    def create_folded_figured_based_on_clicks_in_unfolded_figure(
            self, unfolded_figure):
        # Setup empty period recording clicks for folding.
        event_coordinates = []
        event_coordinates_data_source = ColumnDataSource({
            'Time (BTJD)': [],
            'Relative flux': []
        })
        unfolded_figure.circle('Time (BTJD)',
                               'Relative flux',
                               source=event_coordinates_data_source,
                               color='red',
                               alpha=0.8)  # Will be updated.
        # Prepare the folded plot.
        folded_data_source = ColumnDataSource({
            'Relative flux': self.relative_fluxes,
            'Folded time (days)': [],
            'Time (BTJD)': self.times
        })
        folded_figure = Figure(x_axis_label='Folded time (days)',
                               y_axis_label='Relative flux',
                               title=f'Folded {self.title}')
        self.plot_light_curve_source(folded_figure,
                                     folded_data_source,
                                     time_column_name='Folded time (days)')
        folded_figure.sizing_mode = 'stretch_width'
        self_ = self

        def click_unfolded_figure_callback(
                tap_event):  # Setup what should happen when a click occurs.
            event_coordinate = tap_event.x, tap_event.y
            event_coordinates.append(event_coordinate)
            event_coordinates_data_source.data = {
                'Time (BTJD)':
                [coordinate[0] for coordinate in event_coordinates],
                'Relative flux':
                [coordinate[1] for coordinate in event_coordinates]
            }
            if len(
                    event_coordinates
            ) > 1:  # If we have more than 1 period click, we can start folding.
                event_times = [
                    coordinate[0] for coordinate in event_coordinates
                ]
                epoch, period = self.calculate_epoch_and_period_from_approximate_event_times(
                    event_times)
                folded_times = self.fold_times(self_.times, epoch, period)
                folded_data_source.data['Folded time (days)'] = folded_times
                # folded_figure.x_range.start = -period/10
                # folded_figure.x_range.end = period/10
                self_.period = period
                self_.transit_epoch = epoch
                period_depths = [
                    coordinate[1] for coordinate in event_coordinates
                ]
                self_.depth = np.abs(np.mean(period_depths))

        unfolded_figure.on_event(Tap, click_unfolded_figure_callback)
        return folded_figure
Exemple #2
0
 def create_unfolded_light_curve_figure(self, data_source):
     figure = Figure(title='Unfolded light curve',
                     x_axis_label='Time (BTJD)',
                     y_axis_label='Normalized PDCSAP flux',
                     active_drag='box_zoom')
     self.plot_folding_colored_light_curve_source(figure, data_source)
     figure.sizing_mode = 'stretch_width'
     return figure
Exemple #3
0
    def create_folded_figured_based_on_clicks_in_unfolded_figure(
            self, unfolded_figure, data_source):
        # Setup empty period recording clicks for folding.
        unfolded_figure.circle('Time (BTJD)',
                               'Normalized PDCSAP flux',
                               source=self.fold_coordinate_data_source,
                               color='red',
                               alpha=0.8)  # Will be updated.
        # Prepare the folded plot.
        folded_figure = Figure(x_axis_label='Folded time (days)',
                               y_axis_label='Normalized PDCSAP flux',
                               title=f'Folded light curve')
        self.plot_folding_colored_light_curve_source(
            folded_figure, data_source, time_column_name='Folded time (days)')
        folded_figure.sizing_mode = 'stretch_width'
        self_ = self

        @gen.coroutine
        def update_click_dots():
            self.fold_coordinate_data_source.data = {
                'Time (BTJD)':
                [coordinate[0] for coordinate in self.event_coordinates],
                'Normalized PDCSAP flux':
                [coordinate[1] for coordinate in self.event_coordinates]
            }

        @gen.coroutine
        def update_folded_figure(folded_times):
            data_source.data['Folded time (days)'] = folded_times

        @gen.coroutine
        @without_document_lock
        def click_unfolded_figure_callback(
                tap_event):  # Setup what should happen when a click occurs.
            event_coordinate = tap_event.x, tap_event.y
            self.event_coordinates.append(event_coordinate)
            self.bokeh_document.add_next_tick_callback(update_click_dots)
            if len(
                    self.event_coordinates
            ) > 1:  # If we have more than 1 period click, we can start folding.
                event_times = [
                    coordinate[0] for coordinate in self.event_coordinates
                ]
                epoch, period = self.calculate_epoch_and_period_from_approximate_event_times(
                    event_times)
                folded_times = self.fold_times(data_source.data['Time (BTJD)'],
                                               epoch, period)
                self.bokeh_document.add_next_tick_callback(
                    partial(update_folded_figure, folded_times=folded_times))
                self_.period = period
                self_.transit_epoch = epoch
                period_depths = [
                    coordinate[1] for coordinate in self.event_coordinates
                ]
                self_.depth = np.abs(np.mean(period_depths))

        unfolded_figure.on_event(Tap, click_unfolded_figure_callback)
        return folded_figure
Exemple #4
0
 def create_light_curve_figure(self):
     figure = Figure(title=self.title,
                     x_axis_label='Time (BTJD)',
                     y_axis_label='Relative flux',
                     active_drag='box_zoom')
     data_source = ColumnDataSource({
         'Time (BTJD)': self.times,
         'Relative flux': self.relative_fluxes
     })
     self.plot_light_curve_source(figure, data_source)
     figure.sizing_mode = 'stretch_width'
     return figure
Exemple #5
0
def view_database_file(_id, file_type):

    ds = load_file(_id, file_type)
    d = {}
    for data_var in ds.data_vars:
        d[data_var] = [ds[data_var].values]
    d["to_plot"] = [ds[list(ds.data_vars)[0]].values]
    source = ColumnDataSource(d)

    callback = CustomJS(
        args=dict(source=source),
        code="""
        var data = source.data;
        data['to_plot'] = data[cb_obj.value];
        source.change.emit();
    """,
    )
    select = Select(title="Variable:", options=list(ds.data_vars))
    select.js_on_change("value", callback)

    p = Figure(
        x_range=(-180, 180),
        y_range=(-90, 90),
        aspect_ratio=2.5,
        tools="pan,wheel_zoom,box_zoom,reset, hover",
    )
    p.sizing_mode = "scale_width"
    p.image(
        image="to_plot",
        x=-180,
        y=-90,
        dw=360,
        dh=180,
        source=source,
        palette="Viridis11",
    )
    script, div = components(
        column(column(select), p, sizing_mode="stretch_width"))

    return render_template(
        "app/bokeh_plot.html",
        script=script,
        div=div,
        data_file_id=_id,
        title=file_type.capitalize(),
    )
Exemple #6
0
    def show_light_curve(self, light_curve_path: Path):
        """
        Shows a figure of the light curve at the passed path.

        :param light_curve_path: The path of the light curve.
        """
        fluxes, times = self.load_fluxes_and_times_from_fits_file(
            light_curve_path)
        figure = Figure(title=str(light_curve_path),
                        x_axis_label='Flux',
                        y_axis_label='Time',
                        active_drag='box_zoom')
        color = 'mediumblue'
        figure.line(times, fluxes, line_color=color, line_alpha=0.1)
        figure.circle(times,
                      fluxes,
                      line_color=color,
                      line_alpha=0.4,
                      fill_color=color,
                      fill_alpha=0.1)
        figure.sizing_mode = 'stretch_width'
        show(figure)
Exemple #7
0
    def create_flux_comparison_light_curve_figure(
    ) -> (Figure, ColumnDataSource):
        figure = Figure(title='Normalized flux comparison',
                        x_axis_label='Time (BTJD)',
                        y_axis_label='Normalized flux',
                        active_drag='box_zoom')
        data_source = ColumnDataSource({
            'Time (BTJD)': [],
            'Normalized PDCSAP flux': [],
            'Normalized SAP flux': [],
            'Time (days)': [],
            'Folded time (days)': []
        })

        def add_light_curve(times_column_name, fluxes_column_name,
                            legend_label, color):
            """Adds a light curve to the figure."""
            figure.line(source=data_source,
                        x=times_column_name,
                        y=fluxes_column_name,
                        line_color=color,
                        line_alpha=0.1)
            figure.circle(source=data_source,
                          x=times_column_name,
                          y=fluxes_column_name,
                          legend_label=legend_label,
                          line_color=color,
                          line_alpha=0.4,
                          fill_color=color,
                          fill_alpha=0.1)

        add_light_curve('Time (BTJD)', 'Normalized PDCSAP flux', 'PDCSAP',
                        'firebrick')
        add_light_curve('Time (BTJD)', 'Normalized SAP flux', 'SAP',
                        'mediumblue')
        figure.sizing_mode = 'stretch_width'
        return figure, data_source
Exemple #8
0
    def create_mcmc_fit_figures(self, run_fitting_button):
        self_ = self
        initial_fit_figure = Figure(x_axis_label='Folded time (days)',
                                    y_axis_label='Relative flux',
                                    title=f'Initial fit')
        parameters_table_columns = [
            TableColumn(field=column, title=column)
            for column in ['parameter', 'mean', 'sd', 'r_hat']
        ]
        parameters_table = DataTable(source=self.parameters_table_data_source,
                                     columns=parameters_table_columns,
                                     editable=True)
        initial_fit_data_source = self.initial_fit_data_source
        bokeh_document = self.bokeh_document

        @gen.coroutine
        def update_initial_fit_figure(fluxes, gp_pred, inds, lc_pred, map_soln,
                                      relative_times, times, x_fold):
            initial_fit_data_source.data['Time (BTJD)'] = times
            initial_fit_data_source.data['Time (days)'] = relative_times
            initial_fit_data_source.data['Folded time (days)'] = x_fold
            initial_fit_data_source.data[
                'Relative flux'] = fluxes - gp_pred - map_soln["mean"]
            initial_fit_data_source.data[
                'Fit'] = lc_pred[inds] - map_soln["mean"]
            initial_fit_data_source.data['Fit time'] = x_fold[
                inds]  # TODO: This is terrible, you should be able to line them up *afterward* to not make a duplicate time column

        @gen.coroutine
        @without_document_lock
        def fit(self, map_soln, model):
            with model:
                trace = pm.sample(
                    tune=2000,
                    draws=2000,
                    start=map_soln,
                    chains=4,
                    step=xo.get_dense_nuts_step(target_accept=0.9),
                )
            trace_summary = pm.summary(
                trace, round_to='none'
            )  # Not a typo. PyMC3 wants 'none' as a string here.
            epoch = round(
                trace_summary['mean']['Transit epoch (BTJD)'],
                3)  # Round the epoch differently, as BTJD needs more digits.
            trace_summary['mean'] = self_.round_series_to_significant_figures(
                trace_summary['mean'], 5)
            trace_summary['mean']['Transit epoch (BTJD)'] = epoch
            self.bokeh_document.add_next_tick_callback(
                partial(self.update_parameters_table, trace_summary))
            with pd.option_context('display.max_columns', None,
                                   'display.max_rows', None):
                print(trace_summary)
                print(f'Star radius: {self.star_radius}')
            # TODO: This should not happen automatically. Only after a button click.
            # scopes = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive']
            # credentials = Credentials.from_service_account_file(
            #     'ramjet/analysis/google_spreadsheet_credentials.json', scopes=scopes)
            # gc = gspread.authorize(credentials)
            # sh = gc.open('Ramjet transit candidates shared for vetting')
            # worksheet = sh.get_worksheet(0)
            # # Find first empty row.
            # empty_row_index = 1
            # for row_index in itertools.count(start=1):
            #     row_values = worksheet.row_values(row_index)
            #     if len(row_values) == 0:
            #         empty_row_index = row_index
            #         break
            # worksheet.update_cell(empty_row_index, 1, self_.tic_id)
            # worksheet.update_cell(empty_row_index, 2, str(self_.sectors).replace('[', '').replace(']', '')),
            # worksheet.update_cell(empty_row_index, 3, trace_summary['mean']['Transit epoch (BTJD)'])
            # worksheet.update_cell(empty_row_index, 4, trace_summary['mean']['period'])
            # worksheet.update_cell(empty_row_index, 5, trace_summary['mean']['Transit depth (relative flux)'])
            # worksheet.update_cell(empty_row_index, 6, trace_summary['mean']['Transit duration (days)'])
            # worksheet.update_cell(empty_row_index, 7, self_.star_radius)
            # worksheet.update_cell(empty_row_index, 8, trace_summary['mean']['Planet radius (solar radii)'] * self_.star_radius)

        @gen.coroutine
        @without_document_lock
        def run_fitting():
            times = self.light_curve_data_source.data['Time (BTJD)'].astype(
                np.float32)
            flux_errors = self.light_curve_data_source.data[
                'Normalized PDCSAP flux error']
            fluxes = self.light_curve_data_source.data[
                'Normalized PDCSAP flux']
            relative_times = self.light_curve_data_source.data['Time (days)']
            nan_indexes = np.union1d(
                np.argwhere(np.isnan(fluxes)),
                np.union1d(np.argwhere(np.isnan(times)),
                           np.argwhere(np.isnan(flux_errors))))
            fluxes = np.delete(fluxes, nan_indexes)
            flux_errors = np.delete(flux_errors, nan_indexes)
            times = np.delete(times, nan_indexes)
            relative_times = np.delete(relative_times, nan_indexes)
            with pm.Model() as model:
                # Stellar parameters
                mean = pm.Normal("mean", mu=0.0, sigma=10.0 * 1e-3)
                u = xo.distributions.QuadLimbDark("u")
                star_params = [mean, u]

                # Gaussian process noise model
                sigma = pm.InverseGamma("sigma",
                                        alpha=3.0,
                                        beta=2 * np.nanmedian(flux_errors))
                log_Sw4 = pm.Normal("log_Sw4", mu=0.0, sigma=10.0)
                log_w0 = pm.Normal("log_w0",
                                   mu=np.log(2 * np.pi / 10.0),
                                   sigma=10.0)
                kernel = xo.gp.terms.SHOTerm(log_Sw4=log_Sw4,
                                             log_w0=log_w0,
                                             Q=1.0 / 3)
                noise_params = [sigma, log_Sw4, log_w0]

                # Planet parameters
                log_ror = pm.Normal("log_ror",
                                    mu=0.5 * np.log(self_.depth),
                                    sigma=10.0 * 1e-3)
                ror = pm.Deterministic("ror", tt.exp(log_ror))
                depth = pm.Deterministic('Transit depth (relative flux)',
                                         tt.square(ror))
                planet_radius = pm.Deterministic('Planet radius (solar radii)',
                                                 ror * self_.star_radius)

                # Orbital parameters
                log_period = pm.Normal("log_period",
                                       mu=np.log(self_.period),
                                       sigma=1.0)
                t0 = pm.Normal('Transit epoch (BTJD)',
                               mu=self_.transit_epoch,
                               sigma=1.0)
                log_dur = pm.Normal("log_dur", mu=np.log(0.1), sigma=10.0)
                b = xo.distributions.ImpactParameter("b", ror=ror)

                period = pm.Deterministic('Transit period (days)',
                                          tt.exp(log_period))
                dur = pm.Deterministic('Transit duration (days)',
                                       tt.exp(log_dur))

                # Set up the orbit
                orbit = xo.orbits.KeplerianOrbit(period=period,
                                                 duration=dur,
                                                 t0=t0,
                                                 b=b,
                                                 r_star=self.star_radius)

                # We're going to track the implied density for reasons that will become clear later
                pm.Deterministic("rho_circ", orbit.rho_star)

                # Set up the mean transit model
                star = xo.LimbDarkLightCurve(u)

                def lc_model(t):
                    return mean + tt.sum(star.get_light_curve(
                        orbit=orbit, r=ror * self.star_radius, t=t),
                                         axis=-1)

                # Finally the GP observation model
                gp = xo.gp.GP(kernel,
                              times, (flux_errors**2) + (sigma**2),
                              mean=lc_model)
                gp.marginal("obs", observed=fluxes)

                # Double check that everything looks good - we shouldn't see any NaNs!
                print(model.check_test_point())

                # Optimize the model
                map_soln = model.test_point
                map_soln = xo.optimize(map_soln, [sigma])
                map_soln = xo.optimize(map_soln, [log_ror, b, log_dur])
                map_soln = xo.optimize(map_soln, noise_params)
                map_soln = xo.optimize(map_soln, star_params)
                map_soln = xo.optimize(map_soln)

            with model:
                gp_pred, lc_pred = xo.eval_in_model(
                    [gp.predict(), lc_model(times)], map_soln)

            x_fold = (times - map_soln['Transit epoch (BTJD)'] +
                      0.5 * map_soln['Transit period (days)']
                      ) % map_soln['Transit period (days)'] - 0.5 * map_soln[
                          'Transit period (days)']
            inds = np.argsort(x_fold)
            bokeh_document.add_next_tick_callback(
                partial(update_initial_fit_figure, fluxes, gp_pred, inds,
                        lc_pred, map_soln, relative_times, times, x_fold))

            self.bokeh_document.add_next_tick_callback(
                partial(fit, self, map_soln, model))

        run_fitting_button.on_click(run_fitting)
        self.plot_folding_colored_light_curve_source(
            initial_fit_figure,
            self.initial_fit_data_source,
            time_column_name='Folded time (days)',
            flux_column_name='Relative flux')
        initial_fit_figure.line('Fit time',
                                'Fit',
                                source=self.initial_fit_data_source,
                                color='black',
                                line_width=3)
        initial_fit_figure.sizing_mode = 'stretch_width'

        return initial_fit_figure, parameters_table