Exemplo n.º 1
0
    def eclipse_model(self, xi):
        r"""
        Compute eclipse model at orbital phases ``xi``.

        Parameters
        ----------
        xi : `~numpy.ndarray`
            Orbital phase angle :math:`\xi`

        Returns
        -------
        eclipse : `~numpy.ndarray`
            Eclipse model normalized such that flux is zero in eclipse.
        """
        from batman import TransitModel

        xi_over_pi = xi / np.pi
        eclipse = TransitModel(
            self,
            xi_over_pi,
            transittype='secondary',
            exp_time=xi_over_pi[1] - xi_over_pi[0],
            supersample_factor=3,
        ).light_curve(self)
        eclipse -= eclipse.min()
        return eclipse
Exemplo n.º 2
0
def transit_model(theta, t, exp_time=0.002):
    params = TransitParams()
    t0, p, k, r, b, q1, q2 = theta[:7]
    u1, u2 = q_to_u(q1, q2)
    a = arstar(p, r)
    i = inclination(a, b)
    params.t0 = t0  #time of inferior conjunction
    params.per = p  #orbital period
    params.rp = k  #planet radius (in units of stellar radii)
    params.a = a  #semi-major axis (in units of stellar radii)
    params.inc = i  #orbital inclination (in degrees)
    params.ecc = 0.  #eccentricity
    params.w = 90.  #longitude of periastron (in degrees)
    params.u = [u1, u2]  #limb darkening coefficients
    params.limb_dark = "quadratic"
    m = TransitModel(params, t, supersample_factor=1, exp_time=exp_time)
    # m = TransitModel(params, t)
    return m.light_curve(params)
Exemplo n.º 3
0
def test_phase_curve(depth_frac):
    """
    from linea.tests.test_core import test_phase_curve as f; f()
    """
    p = Planet.from_name("55 Cnc e")

    ra = generate_recarray_55Cnce(sinusoid_amp_depth_frac=depth_frac)
    lc = CheopsLightCurve(ra, norm=True)

    # check that normalization brings flux continuum to unity:
    assert abs(np.median(lc.flux) - 1) < 1e-5

    lc.sigma_clip_flux()

    # Check that some points get masked by the flux sigma clipping
    assert np.count_nonzero(lc.mask) > 0

    transit_model = TransitModel(
        p,
        lc.bjd_time[~lc.mask],
        supersample_factor=3,
        exp_time=lc.bjd_time[1] - lc.bjd_time[0],
    ).light_curve(p) - 1

    t = lc.bjd_time[~lc.mask, None] - lc.bjd_time.mean()

    # Build a design matrix
    X = np.hstack([
        # Transit model:
        transit_model[:, None],

        # Sinusoidal phase curve trend:
        np.sin(2 * np.pi * t / p.per),
        np.cos(2 * np.pi * t / p.per),

        # Default design matrix:
        lc.design_matrix(),
    ])

    r = lc.regress(X)

    sinusoid = X[:, 1:3] @ r.betas[1:3]

    phase_curve_amp_ppm = 1e6 * sinusoid.ptp()
    phase_curve_amp_error_ppm = 1e6 * np.max(np.sqrt(np.diag(r.cov))[1:3])

    true_amp_ppm = 2e6 * p.rp**2 * depth_frac

    # Check that phase curve amplitude is within 2 sigma of the true answer
    agreement_sigma = (abs(phase_curve_amp_ppm - true_amp_ppm) /
                       phase_curve_amp_error_ppm)
    assert agreement_sigma < 2
Exemplo n.º 4
0
def test_eclipse(eclipse_depth):
    """
    from linea.tests.test_core import test_eclipse as f; f()
    """
    p = Planet.from_name("WASP-189 b")

    ras = generate_recarrays_WASP189(depth_ppm=eclipse_depth)
    lcs = JointLightCurve([CheopsLightCurve(ra) for ra in ras])

    # check that normalization brings flux continuum to unity:
    assert all([abs(np.median(lc.flux) - 1) < 1e-5 for lc in lcs])

    for lc in lcs:
        lc.sigma_clip_flux()

    # Check that some points get masked by the flux sigma clipping
    assert all([np.count_nonzero(lc.mask) > 0 for lc in lcs])

    all_lcs = lcs.concatenate()
    eclipse_model = TransitModel(
        p,
        all_lcs.bjd_time[~all_lcs.mask],
        supersample_factor=3,
        transittype='secondary',
        exp_time=lcs[0].bjd_time[1] - lcs[0].bjd_time[0],
    ).light_curve(p) - 1

    X_combined = lcs.combined_design_matrix()

    # Build a design matrix
    X = np.hstack([
        # Default combined design matrix:
        X_combined,

        # Eclipse model
        eclipse_model[:, None]
    ])

    r = lcs.regress(X)

    obs_eclipse_depth = r.betas[-1]
    eclipse_error_ppm = np.sqrt(np.diag(r.cov))[-1]

    # Check that eclipse depth is within 2 sigma of the true answer
    agreement_sigma = (abs(obs_eclipse_depth - eclipse_depth) /
                       eclipse_error_ppm)

    assert agreement_sigma < 2
Exemplo n.º 5
0
def init_batman(t, init_params, exp_time=0.000694):

    a = inclination(init_params['p'], init_params['r'])
    i = inclination(a, init_params['b'])
    params = TransitParams()
    params.t0 = init_params['t0']     #time of inferior conjunction
    params.per = init_params['p']     #orbital period
    params.rp = init_params['k']      #planet radius (in units of stellar radii)
    params.a = a       #semi-major axis (in units of stellar radii)
    params.inc = i               #orbital inclination (in degrees)
    params.ecc = 0.                         #eccentricity
    params.w = 90.                          #longitude of periastron (in degrees)
    u1, u2 = q_to_u(init_params['q1'], init_params['q2'])
    params.u = [u1, u2]                     #limb darkening coefficients
    params.limb_dark = "quadratic"

    tm = TransitModel(params, t, supersample_factor=1, exp_time=exp_time)
    return tm
Exemplo n.º 6
0
def generate_recarrays_WASP189(depth_ppm=80,
                               seed=42,
                               n_outliers=50,
                               cheops_orbit_min=99.5,
                               obs_efficiency=0.55,
                               n_visits=4):
    p = Planet.from_name("WASP-189 b")

    np.random.seed(seed)

    record_arrays = []

    for i in range(n_visits):

        all_times = np.linspace(p.t0 + p.per * (0.5 + i) - 0.1 * p.per,
                                p.t0 + p.per * (0.5 + i) + 0.1 * p.per, 2500)
        cheops_phase = (((all_times[0] - all_times) * u.day) %
                        (cheops_orbit_min * u.min) /
                        (cheops_orbit_min * u.min)).to(
                            u.dimensionless_unscaled).value

        observable = cheops_phase > (1 - obs_efficiency)
        bjd_time = all_times[observable]
        utc_time = Time(bjd_time, format='jd').isot
        mjd_time = Time(bjd_time, format='jd').mjd

        n_points = len(bjd_time)

        conta_lc = 10 * np.ones(n_points) + np.random.randn(n_points)
        conta_lc_err = np.ones(n_points) / 100
        status = np.zeros(n_points)
        event = np.zeros(n_points)
        dark = 10 + np.random.randn(n_points)
        background = 100 + np.random.randn(n_points)
        roll_angle = simulate_roll_angle(cheops_phase[observable],
                                         bjd_time,
                                         phase_offset=0.1)
        location_x = 512 * np.ones(n_points)
        location_y = 512 * np.ones(n_points)
        centroid_x = location_x[0] + 0.2 * np.random.randn(n_points)
        centroid_y = location_y[0] + 0.2 * np.random.randn(n_points)

        p.fp = depth_ppm * 1e-6
        model = TransitModel(p,
                             bjd_time,
                             supersample_factor=3,
                             transittype='secondary',
                             exp_time=bjd_time[1] - bjd_time[0]).light_curve(p)

        basis_vectors = ((bjd_time - bjd_time.mean()), background, conta_lc,
                         dark, roll_angle / 360, (centroid_x - location_x)**2,
                         (centroid_y - location_y)**2,
                         (centroid_x - location_x), (centroid_y - location_y),
                         dark.mean())

        flux = np.zeros_like(bjd_time)

        for c, v in zip(true_basis_vector_weights, basis_vectors):
            flux += c * v

        flux *= model

        fluxerr = np.std(flux) * np.ones(len(bjd_time))

        rand_indices = np.random.randint(0, flux.shape[0], size=n_outliers)
        flux[rand_indices] += 3.5 * flux.std() * np.random.randn(n_outliers)

        formatter = [('UTC_TIME', '|S26', utc_time),
                     ('MJD_TIME', '>f8', mjd_time),
                     ('BJD_TIME', '>f8', bjd_time), ('FLUX', '>f8', flux),
                     ('FLUXERR', '>f8', fluxerr), ('STATUS', '>i4', status),
                     ('EVENT', '>i4', event), ('DARK', '>f8', dark),
                     ('BACKGROUND', '>f8', background),
                     ('CONTA_LC', '>f8', conta_lc),
                     ('CONTA_LC_ERR', '>f8', conta_lc_err),
                     ('ROLL_ANGLE', '>f8', roll_angle),
                     ('LOCATION_X', '>f4', location_x),
                     ('LOCATION_Y', '>f4', location_y),
                     ('CENTROID_X', '>f4', centroid_x),
                     ('CENTROID_Y', '>f4', centroid_y)]

        ra = np.recarray((n_points, ),
                         names=[name for name, _, _ in formatter],
                         formats=[fmt for _, fmt, _ in formatter])

        for name, fmt, arr in formatter:
            ra[name] = arr
        record_arrays.append(ra)

    return [fitsrec.FITS_rec(ra) for ra in record_arrays]
Exemplo n.º 7
0
            target_inds_observed.update(object_inds_to_observe)

            # Split the night evenly into N chunks
            try:
                split_times = np.split(times, n_objects_per_night)
            except ValueError:
                split_times = np.split(
                    times[len(times) % n_objects_per_night:],
                    n_objects_per_night)

            for i, ind in enumerate(object_inds_to_observe):
                obs_times = split_times[i].jd
                target_name = table['spc'][ind]
                params.inc = table['inclination'][ind]
                params.t0 = table['epoch'][ind]
                transit_model = TransitModel(params,
                                             obs_times).light_curve(params)

                # Only save LCs containing transits
                if transit_model.min() < 0.995:
                    random_night = np.random.randint(0, len(real_lcs))
                    obs_fluxes = transit_model * np.tile(
                        real_lcs[random_night].T, 2).T[:len(obs_times), 1]

                    obs_database[target_name]['times'].append(obs_times)
                    obs_database[target_name]['fluxes'].append(obs_fluxes)
                    obs_database[target_name]['model'].append(transit_model)
                    obs_database[target_name]['transit'] = True

    N_transits = 0
    for key in obs_database:
        if obs_database[key]['transit']:
Exemplo n.º 8
0
    def light_curve(self,
                    spot_lons,
                    spot_lats,
                    spot_radii,
                    inc_stellar,
                    planet=None,
                    times=None,
                    fast=False,
                    time_ref=None,
                    return_spots_occulted=False,
                    transit_model_kwargs={}):
        """
        Generate a(n ensemble of) light curve(s).

        Light curve output will have shape ``(n_phases, len(inc_stellar))`` or
        ``(len(times), len(inc_stellar))``.

        Parameters
        ----------
        spot_lons : `~numpy.ndarray`
            Spot longitudes
        spot_lats : `~numpy.ndarray`
            Spot latitudes
        spot_radii : `~numpy.ndarray`
            Spot radii
        inc_stellar : `~numpy.ndarray`
            Stellar inclinations
        planet : `~batman.TransitParams`, optional
            Transiting planet parameters
        times : `~numpy.ndarray`, optional
            Times at which to compute the light curve
        fast : bool, optional
            When `True`, use approximation that spots are fixed on the star
            during a transit event. When `False`, account for motion of
            starspots on stellar surface due to rotation during transit event.
            Default is `False`.
        time_ref : float, optional
            Reference time used as the initial rotational phase of the star,
            such that the sub-observer point is at zero longitude at
            ``time_ref``.
        return_spots_occulted : bool, optional
            Return whether or not spots have been occulted.

        Returns
        -------
        light_curves : `~numpy.ndarray`
            Stellar light curves of shape ``(n_phases, len(inc_stellar))`` or
            ``(len(times), len(inc_stellar))``
        """
        if time_ref is None:
            if times is not None:
                time_ref = 0
            else:
                time_ref = self.phases[0]

        # Compute the spot positions in cartesian coordinates:
        tilted_spots = self.spherical_to_cartesian(spot_lons,
                                                   spot_lats,
                                                   inc_stellar,
                                                   times=times,
                                                   planet=planet,
                                                   time_ref=time_ref)

        # Compute the distance of each spot from the stellar centroid, mask
        # any spots that are "behind" the star, in other words, x < 0
        r = np.ma.masked_array(np.hypot(tilted_spots.y.value,
                                        tilted_spots.z.value),
                               mask=tilted_spots.x.value < 0)
        ld = limb_darkening_normed(self.u_ld, r)

        # Compute the out-of-transit flux missing due to each spot
        f_spots = (np.pi * spot_radii**2 * (1 - self.spot_contrast) * ld *
                   np.sqrt(1 - r**2))

        if planet is None:
            # If there is no transiting planet, skip the transit routine:
            lambda_e = np.zeros((len(self.phases), 1))
        else:
            if not inc_stellar.isscalar:
                raise ValueError('Transiting exoplanets are implemented for '
                                 'planets transiting single stars only, but '
                                 '``inc_stellar`` has multiple values. ')
            # Compute a transit model
            from batman import TransitModel

            n_spots = len(spot_lons)
            m = TransitModel(planet, times, **transit_model_kwargs)
            lambda_e = 1 - m.light_curve(planet)[:, np.newaxis]
            # Compute the true anomaly of the planet at each time, f:
            f = m.get_true_anomaly()

            # Compute the position of the planet in cartesian coordinates using
            # Equations 53-55 of Murray & Correia (2010). Note that these
            # coordinates are different from the cartesian coordinates used for
            # the spot positions. In this system, the observer is at X-> -inf.
            I = np.radians(90 - planet.inc)
            Omega = np.radians(planet.w)  # this is 90 deg by default
            omega = np.pi / 2
            X = planet.a * (np.cos(Omega) * np.cos(omega + f) -
                            np.sin(Omega) * np.sin(omega + f) * np.cos(I))
            Y = planet.a * (np.sin(Omega) * np.cos(omega + f) +
                            np.cos(Omega) * np.sin(omega + f) * np.cos(I))
            Z = planet.a * np.sin(omega + f) * np.sin(I)

            # Create a shapely circle object for the planet's silhouette only
            # when the planet is in front of the star, otherwise append `None`
            planet_disk = [
                circle([-Y[i], -Z[i]], planet.rp) if
                (np.abs(Y[i]) < 1 + planet.rp) and (X[i] < 0) else None
                for i in range(len(f))
            ]

            if fast:
                spots_occulted = self._planet_spot_overlap_fast(
                    planet, planet_disk, tilted_spots, spot_radii, n_spots, X,
                    Y, lambda_e)
            else:
                spots_occulted = self._planet_spot_overlap_slow(
                    planet, planet_disk, tilted_spots, spot_radii, n_spots, X,
                    Y, lambda_e)

        # Return the flux missing from the star at each time due to spots
        # (f_spots/self.f0) and due to the transit (lambda_e):
        if return_spots_occulted:
            return (1 - np.sum(f_spots.filled(0) / self.f0, axis=1) - lambda_e,
                    spots_occulted)

        else:
            return 1 - np.sum(f_spots.filled(0) / self.f0, axis=1) - lambda_e
Exemplo n.º 9
0
def generate_recarray_55Cnce(seed=42,
                             sinusoid_amp_depth_frac=0.25,
                             n_outliers=50,
                             cheops_orbit_min=99.5,
                             obs_efficiency=0.5):
    p = Planet.from_name("55 Cnc e")

    np.random.seed(seed)

    all_times = np.linspace(p.t0 - 0.5, p.t0 + 1., 2500)
    cheops_phase = (((all_times[0] - all_times) * u.day) %
                    (cheops_orbit_min * u.min) /
                    (cheops_orbit_min * u.min)).to(
                        u.dimensionless_unscaled).value

    bjd_time = all_times[cheops_phase > (1 - obs_efficiency)]
    utc_time = Time(bjd_time, format='jd').isot
    mjd_time = Time(bjd_time, format='jd').mjd

    n_points = len(bjd_time)

    conta_lc = 10 * np.ones(n_points) + np.random.randn(n_points)
    conta_lc_err = np.ones(n_points) / 100
    status = np.zeros(n_points)
    event = np.zeros(n_points)
    dark = 10 + np.random.randn(n_points)
    background = 100 + np.random.randn(n_points)
    roll_angle = simulate_roll_angle(cheops_phase[cheops_phase > 0.5],
                                     bjd_time)
    location_x = 512 * np.ones(n_points)
    location_y = 512 * np.ones(n_points)
    centroid_x = location_x[0] + 0.2 * np.random.randn(n_points)
    centroid_y = location_y[0] + 0.2 * np.random.randn(n_points)

    model = TransitModel(p,
                         bjd_time,
                         supersample_factor=3,
                         exp_time=bjd_time[1] - bjd_time[0]).light_curve(p)
    planet_phase = ((bjd_time - p.t0) % p.per) / p.per

    sinusoid_amp = sinusoid_amp_depth_frac * p.rp**2

    model += sinusoid_amp * np.sin(2 * np.pi * (planet_phase + 0.5))

    basis_vectors = (np.cos(np.radians(roll_angle)),
                     np.sin(np.radians(roll_angle)), np.ones(len(roll_angle)))

    flux = np.zeros_like(bjd_time)

    for c, v in zip(true_basis_vector_weights, basis_vectors):
        flux += c * v

    flux *= model

    fluxerr = np.std(flux) * np.ones(len(bjd_time))

    rand_indices = np.random.randint(0, flux.shape[0], size=n_outliers)
    flux[rand_indices] += 3.5 * flux.std() * np.random.randn(n_outliers)

    formatter = [('UTC_TIME', '|S26', utc_time), ('MJD_TIME', '>f8', mjd_time),
                 ('BJD_TIME', '>f8', bjd_time), ('FLUX', '>f8', flux),
                 ('FLUXERR', '>f8', fluxerr), ('STATUS', '>i4', status),
                 ('EVENT', '>i4', event), ('DARK', '>f8', dark),
                 ('BACKGROUND', '>f8', background),
                 ('CONTA_LC', '>f8', conta_lc),
                 ('CONTA_LC_ERR', '>f8', conta_lc_err),
                 ('ROLL_ANGLE', '>f8', roll_angle),
                 ('LOCATION_X', '>f4', location_x),
                 ('LOCATION_Y', '>f4', location_y),
                 ('CENTROID_X', '>f4', centroid_x),
                 ('CENTROID_Y', '>f4', centroid_y)]

    ra = np.recarray((n_points, ),
                     names=[name for name, _, _ in formatter],
                     formats=[fmt for _, fmt, _ in formatter])

    for name, fmt, arr in formatter:
        ra[name] = arr

    return fitsrec.FITS_rec(ra)