def minimal_example():
    apo = Observer.at_site('APO', timezone='US/Mountain')
    target = FixedTarget.from_name("HD 209458")

    primary_eclipse_time = Time(2452826.628514, format='jd')
    orbital_period = 3.52474859 * u.day
    eclipse_duration = 0.1277 * u.day

    hd209458 = EclipsingSystem(primary_eclipse_time=primary_eclipse_time,
                               orbital_period=orbital_period,
                               duration=eclipse_duration,
                               name='HD 209458 b')

    n_transits = 100  # This is the roughly number of transits per year

    obs_time = Time('2017-01-01 12:00')
    midtransit_times = hd209458.next_primary_eclipse_time(
        obs_time, n_eclipses=n_transits)

    import astropy.units as u
    min_local_time = dt.time(18, 0)  # 18:00 local time at APO (7pm)
    max_local_time = dt.time(8, 0)  # 08:00 local time at APO (5am)
    constraints = [
        AtNightConstraint.twilight_civil(),
        AltitudeConstraint(min=30 * u.deg),
        LocalTimeConstraint(min=min_local_time, max=max_local_time)
    ]

    # just at midtime
    b = is_event_observable(constraints, apo, target, times=midtransit_times)

    # completely observable transits
    observing_time = Time('2016-01-01 00:00')

    ing_egr = hd209458.next_primary_ingress_egress_time(observing_time,
                                                        n_eclipses=n_transits)

    ibe = is_event_observable(constraints,
                              apo,
                              target,
                              times_ingress_egress=ing_egr)

    oot_duration = 30 * u.minute
    oot_ing_egr = np.concatenate(
        (np.array(ing_egr[:, 0] - oot_duration)[:, None],
         np.array(ing_egr[:, 1] + oot_duration)[:, None]),
        axis=1)

    oibeo = is_event_observable(constraints,
                                apo,
                                target,
                                times_ingress_egress=oot_ing_egr)
Beispiel #2
0
def max_end_time(constraints_list, obs, target, time, dt=1 * u.min):

    time = Time(time)
    morning = obs.sun_rise_time(time, which='next')

    time_grid = np.arange(time.jd, morning.jd, dt.to('day').value) * u.d
    time_grid = Time(time_grid, format='jd')

    index = is_event_observable(constraints_list, obs, target,
                                times=time_grid).squeeze()

    return time_grid[index][-1]
    def target_observable(self, Nights, constraints, delta_midnight=None):
        """
            Calculates for which times during the time span of Nights, the target is observable under the given constraints.
            LATER : Could include plotting of target observability.

            Parameters
            ----------
            Nights : class
                Nights at Paranal for which to compute if the target is observable.
            constraints : list
                list of Astroplan constraints to constrain the observability.
            delta_midnight : numpy.linspace, Obtional
                grid of timesteps within 24 hours for which the observation should be calculated.

        """
        if delta_midnight == None:
            # defines number of timesteps per 24 hours
            delta_midnight = np.linspace(-12, 12, 1000) * u.hour

        print(self.name + ' is getting processed')
        for date in Nights.date:
            """ Check if Nights object has attribute nights to calculate observability of target """
            if hasattr(Nights, 'night'):
                k = list.index(Nights.date, date)
                night = Nights.night[k]
            else:
                Nights.Calculate_nights_paranal(delta_midnight)
                night = Nights.night[k]

            """ Check if target observable """
            tar_obs = astroplan.is_event_observable(
                constraints=constraints, observer=paranal, target=self.Coordinates, times=night)
            if any(tar_obs[0] == True):
                print(
                    '{} Target is observable without any primary eclipse'.format(self.name))
                for n, tar in enumerate(tar_obs[0]):
                    if tar == True:
                        moon_target_sep, moon_phase, airmass, obs_altazs = fun.airmass_moon_sep_obj_altaz(
                            self, night[n])
                        self.target_observable.append({
                            'Name': self.name,
                            'Effective Temperature': self.star_Teff,
                            'J-magnitude': self.star_jmag,
                            'Object observable?': tar,
                            'Obs Data': {'time': night[n],
                                         'airmass': airmass,
                                         'moon sep': moon_target_sep[0],
                                         'moon phase': moon_phase,
                                         'az': obs_altazs.az,
                                         'alt': obs_altazs.alt
                                         }})
Beispiel #4
0
def min_start_time(constraints_list, obs, target, time, dt=1 * u.min):

    # Convert some inputs
    dt = dt.to('day').value
    time = Time(time)

    # Get sun set time occuring before this event
    evening = obs.sun_set_time(time, which='previous')

    time_grid = np.arange(evening.jd, time.jd + dt, dt) * u.d
    time_grid = Time(time_grid, format='jd')

    index = is_event_observable(constraints_list, obs, target,
                                times=time_grid).squeeze()

    return time_grid[index][0]
def get_transit_observability(site,
                              ra,
                              dec,
                              name,
                              t_mid_0,
                              period,
                              duration,
                              n_transits=100,
                              obs_start_time=Time(
                                  dt.datetime.today().isoformat()),
                              min_local_time=dt.time(16, 0),
                              max_local_time=dt.time(9, 0),
                              min_altitude=20 * u.deg,
                              oot_duration=30 * u.minute,
                              minokmoonsep=30 * u.deg):
    """
    note: barycentric corrections not yet implemented. (could do this myself!)
    -> 16 minutes of imprecision is baked into this observability calculator!

    args:

        site (astroplan.observer.Observer)

        ra, dec (units u.deg), e.g.:
            ra=101.28715533*u.deg, dec=16.71611586*u.deg,
        or can also accept
            ra="17 56 35.51", dec="-29 32 21.5"

        name (str), e.g., "Sirius"

        t_mid_0 (float): in BJD_TDB, preferably (but see note above).

        period (astropy quantity, units time)

        duration (astropy quantity, units time)

        n_transits (int): number of transits forward extrapolated to

        obs_start_time (astropy.Time object): when to start calculation from

        min_local_time, max_local_time: earliest time when you think observing
        is OK. E.g., 16:00 local and 09:00 local are earliest and latest. Note
        this constraint is a bit silly, since the astroplan "AtNightConstraint"
        is imposed automatically. As implemented, these are ignored.

        min_altitude (astropy quantity, units deg): 20 degrees is the more
        relevant constraint.

        oot_duration (astropy quantity, units time): with which to brack
        transit observations, to get an OOT baseline.
    """

    if (isinstance(ra, u.quantity.Quantity)
            and isinstance(dec, u.quantity.Quantity)):
        target_coord = SkyCoord(ra=ra, dec=dec)
    elif (isinstance(ra, str) and isinstance(dec, str)):
        target_coord = SkyCoord(ra=ra, dec=dec, unit=(u.hourangle, u.deg))
    else:
        raise NotImplementedError

    target = FixedTarget(coord=target_coord, name=name)

    primary_eclipse_time = Time(t_mid_0, format='jd')

    system = EclipsingSystem(primary_eclipse_time=primary_eclipse_time,
                             orbital_period=period,
                             duration=duration,
                             name=name)

    midtransit_times = system.next_primary_eclipse_time(obs_start_time,
                                                        n_eclipses=n_transits)

    # for the time being, omit any local time constraints.
    constraints = [
        AtNightConstraint.twilight_civil(),
        AltitudeConstraint(min=min_altitude),
        MoonSeparationConstraint(min=minokmoonsep)
    ]
    #constraints = [AtNightConstraint.twilight_civil(),
    #               AltitudeConstraint(min=min_altitude),
    #               LocalTimeConstraint(min=min_local_time, max=max_local_time)]

    # observable just at midtime (bottom)
    b = is_event_observable(constraints, site, target, times=midtransit_times)

    # observable full transits (ingress, bottom, egress)
    ing_egr = system.next_primary_ingress_egress_time(obs_start_time,
                                                      n_eclipses=n_transits)

    ibe = is_event_observable(constraints,
                              site,
                              target,
                              times_ingress_egress=ing_egr)

    # get moon separation over each transit. take minimum moon sep at
    # ing/tmid/egr as the moon separation.
    moon_tmid = get_moon(midtransit_times, location=site.location)
    moon_separation_tmid = moon_tmid.separation(target_coord)

    moon_ing = get_moon(ing_egr[:, 0], location=site.location)
    moon_separation_ing = moon_ing.separation(target_coord)

    moon_egr = get_moon(ing_egr[:, 1], location=site.location)
    moon_separation_egr = moon_egr.separation(target_coord)

    moon_separation = np.round(
        np.array(
            [moon_separation_tmid, moon_separation_ing,
             moon_separation_egr]).min(axis=0), 0).astype(int)

    moon_illumination = np.round(
        100 * moon.moon_illumination(midtransit_times), 0).astype(int)

    # completely observable transits (OOT, ingress, bottom, egress, OOT)
    oot_ing_egr = np.concatenate(
        (np.array(ing_egr[:, 0] - oot_duration)[:, None],
         np.array(ing_egr[:, 1] + oot_duration)[:, None]),
        axis=1)

    oibeo = is_event_observable(constraints,
                                site,
                                target,
                                times_ingress_egress=oot_ing_egr)

    ing_tmid_egr = np.concatenate(
        (np.array(ing_egr[:, 0])[:, None], np.array(midtransit_times)[:, None],
         np.array(ing_egr[:, 1])[:, None]),
        axis=1)

    return ibe, oibeo, ing_tmid_egr, moon_separation, moon_illumination
Beispiel #6
0
    # TODO select proper coordinates: load from params_star.csv
    # TODO select proper observer place (ground, space)
    coords = "1:12:43.2 +1:12:43"
    target = FixedTarget(SkyCoord(coords, unit=(u.deg, u.deg)))
    # TODO select proper time
    n_transits = 100  # This is the roughly number of transits per year
    obs_time = Time('2017-01-01 12:00')
    # TODO bulk to file
    midtransit_times = system.next_primary_eclipse_time(obs_time,
                                                        n_eclipses=n_transits)
    ingress_egress_times = system.next_primary_ingress_egress_time(
        obs_time, n_eclipses=n_transits)
    # TODO select local times somehow
    min_local_time = dt.time(19, 0)  # 19:00 local time at APO (7pm)
    max_local_time = dt.time(0, 0)  # 00:00 local time at APO (midnight)
    constraints = [
        AtNightConstraint.twilight_civil(),
        AltitudeConstraint(min=30 * u.deg),
        LocalTimeConstraint(min=min_local_time, max=max_local_time)
    ]
    midtime_observable = astroplan.is_event_observable(constraints,
                                                       observer_site,
                                                       target,
                                                       times=midtransit_times)
    entire_observable = astroplan.is_event_observable(
        constraints,
        observer_site,
        target,
        times=midtransit_times,
        times_ingress_egress=ingress_egress_times)
def single_planet(data,
                  date_start,
                  date_end,
                  constraints,
                  observer,
                  verbose=0):
    """
    Performs the observability and SNR estimation for a single planet

    Parameters
    ----------
    data : dict
        data for this planet
    date_start : Time
        start date
    date_end : Time
        end date
    constraints : list
        list of observability constraints
    observer : astroplan.Observer
        telescope location
    verbose : int, optional
        how much information to print, by default 0

    Returns
    -------
    result : dict
        contains everything about this planet, one entry per observable transit
    """
    set_verbose_level(verbose)

    primary_eclipse_time = Time(data["pl_tranmid"], format="jd")
    orbital_period = data["pl_orbper"] * u.day
    eclipse_duration = data["pl_trandur"] * u.hour
    name = data["pl_name"]
    # Update the progress bar
    # Define the target coordinates
    coord = SkyCoord(ra=data["ra"], dec=data["dec"], unit=u.deg)
    target = ap.FixedTarget(coord, name=name)
    # Define the star-planet system
    system = ap.EclipsingSystem(
        primary_eclipse_time=primary_eclipse_time,
        orbital_period=orbital_period,
        duration=eclipse_duration,
        name=name,
    )
    # Find all eclipses of this system
    n_max_eclipses = (date_end - date_start) / orbital_period
    n_max_eclipses = max(n_max_eclipses, 1)
    eclipses = system.next_primary_ingress_egress_time(
        date_start, n_eclipses=n_max_eclipses)
    # If the last eclipse is past the final date, just cut it off
    if eclipses[-1][0] > date_end:
        eclipses = eclipses[:-1]
    if len(eclipses) == 0:
        logger.warning(f"No observable transits found for planet {name}")
        return None

    # Check if they are observable
    is_observable = ap.is_event_observable(constraints,
                                           observer,
                                           target,
                                           times_ingress_egress=eclipses)[0]
    observable_eclipses = eclipses[is_observable]
    n_eclipses = len(observable_eclipses)
    if n_eclipses == 0:
        logger.warning(f"No observable transits found for planet {name}")
        return None

    # Calculate the SNR for those that are left
    magnitude = data["sy_kmag"] * u.mag
    exptime = eclipse_duration
    # exptime = np.diff(observable_eclipses.jd, axis=1).ravel() * u.day
    obstime = Time(np.mean(observable_eclipses.jd, axis=1), format="jd")
    airmass = calculate_airmass(obstime, observer, target)
    snr = estimate_snr(magnitude, exptime, airmass)

    # Save results for later
    result = {
        "name": [name] * n_eclipses,
        "snr": snr.to_value(1),
        "exptime": [eclipse_duration.to_value(u.second)] * n_eclipses,
        "time": obstime.mjd,
        "time_begin": observable_eclipses[:, 0].datetime,
        "time_end": observable_eclipses[:, 1].datetime,
        "stellar_effective_temperature": [data["st_teff"]] * n_eclipses,
    }
    return result
def get_event_observability(
    eventclass,
    site, ra, dec, name, t_mid_0, period, duration, n_transits=100,
    obs_start_time=Time(dt.datetime.today().isoformat()),
    min_altitude = None,
    oot_duration = 30*u.minute,
    minokmoonsep = 30*u.deg,
    max_airmass = None,
    twilight_limit = 'nautical'):
    """
    note: barycentric corrections not yet implemented. (could do this myself!)
    -> 16 minutes of imprecision is baked into this observability calculator!

    args:

        eventclass: e.g., "OIBE". Function does NOT return longer events.

        site (astroplan.observer.Observer)

        ra, dec (units u.deg), e.g.:
            ra=101.28715533*u.deg, dec=16.71611586*u.deg,
        or can also accept
            ra="17 56 35.51", dec="-29 32 21.5"

        name (str), e.g., "Sirius"

        t_mid_0 (float): in BJD_TDB, preferably (but see note above).

        period (astropy quantity, units time)

        duration (astropy quantity, units time)

        n_transits (int): number of transits forward extrapolated to

        obs_start_time (astropy.Time object): when to start calculation from

        min_altitude (astropy quantity, units deg): 20 degrees is the more
        relevant constraint.

        max_airmass: e.g., 2.5. One of max_airmass or min_altitude is required.

        oot_duration (astropy quantity, units time): with which to brack
        transit observations, to get an OOT baseline.

        twilight_limit: 'astronomical', 'nautical', 'civil' for -18, -12, -6
        deg.
    """
    if eventclass not in [
        'OIBEO', 'OIBE', 'IBEO', 'IBE', 'BEO', 'OIB', 'OI', 'EO'
    ]:
        raise AssertionError

    if (isinstance(ra, u.quantity.Quantity) and
        isinstance(dec, u.quantity.Quantity)
    ):
        target_coord = SkyCoord(ra=ra, dec=dec)
    elif (isinstance(ra, str) and
          isinstance(dec, str)
    ):
        target_coord = SkyCoord(ra=ra, dec=dec, unit=(u.hourangle, u.deg))
    else:
        raise NotImplementedError

    if (
        not isinstance(max_airmass, float)
        or isinstance(min_altitude, u.quantity.Quantity)
    ):
        raise NotImplementedError

    target = FixedTarget(coord=target_coord, name=name)

    primary_eclipse_time = Time(t_mid_0, format='jd')

    system = EclipsingSystem(primary_eclipse_time=primary_eclipse_time,
                             orbital_period=period, duration=duration,
                             name=name)

    midtransit_times = system.next_primary_eclipse_time(
        obs_start_time, n_eclipses=n_transits)

    # for the time being, omit any local time constraints.
    if twilight_limit == 'astronomical':
        twilight_constraint = AtNightConstraint.twilight_astronomical()
    elif twilight_limit == 'nautical':
        twilight_constraint = AtNightConstraint.twilight_nautical()
    else:
        raise NotImplementedError('civil twilight is janky.')

    constraints = [twilight_constraint,
                   AltitudeConstraint(min=min_altitude),
                   AirmassConstraint(max=max_airmass),
                   MoonSeparationConstraint(min=minokmoonsep)]

    # tabulate ingress and egress times.
    ing_egr = system.next_primary_ingress_egress_time(
        obs_start_time, n_eclipses=n_transits
    )

    oibeo_window = np.concatenate(
        (np.array(ing_egr[:,0] - oot_duration)[:,None],
         np.array(ing_egr[:,1] + oot_duration)[:,None]),
        axis=1)
    oibe_window = np.concatenate(
        (np.array(ing_egr[:,0] - oot_duration)[:,None],
         np.array(ing_egr[:,1])[:,None]),
        axis=1)
    ibeo_window = np.concatenate(
        (np.array(ing_egr[:,0])[:,None],
         np.array(ing_egr[:,1] + oot_duration)[:,None]),
        axis=1)
    oib_window = np.concatenate(
        (np.array(ing_egr[:,0] - oot_duration)[:,None],
         np.array(midtransit_times)[:,None]),
        axis=1)
    beo_window = np.concatenate(
        (np.array(midtransit_times)[:,None],
         np.array(ing_egr[:,1] + oot_duration)[:,None]),
        axis=1)
    ibe_window = ing_egr
    oi_window = np.concatenate(
        (np.array(ing_egr[:,0] - oot_duration)[:,None],
        np.array(ing_egr[:,0])[:,None]),
        axis=1)
    eo_window = np.concatenate(
        (np.array(ing_egr[:,1])[:,None],
        np.array(ing_egr[:,1] + oot_duration)[:,None]),
        axis=1)

    keys = ['oibeo','oibe','ibeo','oib','beo','ibe','oi','eo']
    windows = [oibeo_window, oibe_window, ibeo_window,
               oib_window, beo_window, ibe_window, oi_window, eo_window]
    is_obs_dict = {}
    for key, window in zip(keys, windows):
        is_obs_dict[key] = np.array(
            is_event_observable(constraints, site, target,
                                times_ingress_egress=window)
        ).flatten()

    is_obs_df = pd.DataFrame(is_obs_dict)

    is_obs_df['ing'] = ing_egr[:,0]
    is_obs_df['egr'] = ing_egr[:,1]
    is_obs_df['isoing'] = Time(ing_egr[:,0], format='iso')
    is_obs_df['isoegr'] = Time(ing_egr[:,1], format='iso')

    # this function returns the observable events that are LONGEST. e.g.,
    # during an OIBEO transit you COULD observe just OIB, but why would you?

    if eventclass == 'OIBEO':
        event_ind = np.array(is_obs_df[eventclass.lower()])[None,:]
    elif eventclass in ['IBEO', 'OIBE']:
        event_ind = np.array(
            is_obs_df[eventclass.lower()] & ~is_obs_df['oibeo']
        )[None,:]
    elif eventclass in ['IBE', 'OIB', 'BEO']:
        event_ind = np.array(
            is_obs_df[eventclass.lower()]
            & ~is_obs_df['oibeo']
            & ~is_obs_df['oibe']
            & ~is_obs_df['ibeo']
        )[None,:]
    elif eventclass in ['OI', 'EO']:
        event_ind = np.array(
            is_obs_df[eventclass.lower()]
            & ~is_obs_df['oibeo']
            & ~is_obs_df['oibe']
            & ~is_obs_df['ibeo']
            & ~is_obs_df['oib']
            & ~is_obs_df['ibe']
            & ~is_obs_df['beo']
        )[None,:]

    # get moon separation over each transit. take minimum moon sep at
    # ing/tmid/egr as the moon separation.
    moon_tmid = get_moon(midtransit_times, location=site.location)
    moon_separation_tmid = moon_tmid.separation(target_coord)

    moon_ing = get_moon(ing_egr[:,0], location=site.location)
    moon_separation_ing = moon_ing.separation(target_coord)

    moon_egr = get_moon(ing_egr[:,1], location=site.location)
    moon_separation_egr = moon_egr.separation(target_coord)

    moon_separation = np.round(np.array(
        [moon_separation_tmid, moon_separation_ing,
         moon_separation_egr]).min(axis=0),0).astype(int)

    moon_illumination = np.round(
        100*moon.moon_illumination(midtransit_times),0).astype(int)

    # completely observable transits (OOT, ingress, bottom, egress, OOT)
    oibeo = is_event_observable(constraints, site, target,
                                times_ingress_egress=oibeo_window)

    ing_tmid_egr = np.concatenate(
        (np.array(ing_egr[:,0])[:,None],
         np.array(midtransit_times)[:,None],
         np.array(ing_egr[:,1])[:,None]),
        axis=1)

    target_window = np.array(windows)[
        int(np.argwhere(np.array(keys)==eventclass.lower())), :, :
    ]

    return (
        event_ind, oibeo, ing_tmid_egr, target_window,
        moon_separation, moon_illumination
    )
Beispiel #9
0
def main(args=None):
    p = parser()
    opts = p.parse_args(args)

    # Late imports
    from astroplan import (AirmassConstraint, AtNightConstraint, Observer,
                           is_event_observable)
    from astropy.coordinates import EarthLocation, SkyCoord
    from astropy.time import Time
    from astropy import units as u
    from matplotlib import dates
    from matplotlib import pyplot as plt
    from tqdm import tqdm

    from ..io import fits
    from .. import moc
    from .. import plot  # noqa

    names = ('name', 'longitude', 'latitude', 'height')
    length0, *lengths = (len(getattr(opts, 'site_{}'.format(name)))
                         for name in names)
    if not all(length0 == length for length in lengths):
        p.error('these options require equal numbers of arguments: {}'.format(
            ', '.join('--site-{}'.format(name) for name in names)))

    observers = [Observer.at_site(site) for site in opts.site]
    for name, lon, lat, height in zip(opts.site_name, opts.site_longitude,
                                      opts.site_latitude, opts.site_height):
        location = EarthLocation(lon=lon * u.deg,
                                 lat=lat * u.deg,
                                 height=(height or 0) * u.m)
        observers.append(Observer(location, name=name))
    observers = list(reversed(observers))

    m = fits.read_sky_map(opts.input.name, moc=True)

    t0 = Time(opts.time) if opts.time is not None else Time.now()
    times = t0 + np.linspace(0, 1) * u.day

    theta, phi = moc.uniq2ang(m['UNIQ'])
    coords = SkyCoord(phi, 0.5 * np.pi - theta, unit='rad')
    prob = np.asarray(moc.uniq2pixarea(m['UNIQ']) * m['PROBDENSITY'])

    constraints = [
        getattr(AtNightConstraint, 'twilight_{}'.format(opts.twilight))(),
        AirmassConstraint(opts.max_airmass)
    ]

    fig = plt.figure()
    width, height = fig.get_size_inches()
    fig.set_size_inches(width, (len(observers) + 1) / 16 * width)
    ax = plt.axes()
    locator = dates.AutoDateLocator()
    formatter = dates.DateFormatter('%H:%M')
    ax.set_xlim([times[0].plot_date, times[-1].plot_date])
    ax.xaxis.set_major_formatter(formatter)
    ax.xaxis.set_major_locator(locator)
    ax.set_xlabel("Time from {0} [UTC]".format(min(times).datetime.date()))
    plt.setp(ax.get_xticklabels(), rotation=30, ha='right')
    ax.set_yticks(np.arange(len(observers)))
    ax.set_yticklabels([observer.name for observer in observers])
    ax.yaxis.set_tick_params(left=False)
    ax.grid(axis='x')
    ax.spines['bottom'].set_visible(False)
    ax.spines['top'].set_visible(False)

    for i, observer in enumerate(tqdm(observers)):
        observable = 100 * np.dot(
            prob, is_event_observable(constraints, observer, coords, times))
        ax.contourf(times.plot_date, [i - 0.4, i + 0.4],
                    np.tile(observable, (2, 1)),
                    levels=np.arange(10, 110, 10),
                    cmap=plt.get_cmap().reversed())

    plt.tight_layout()

    # Show or save output.
    opts.output()
Beispiel #10
0
def observable_windows(tforecast,
                       forecast,
                       target,
                       site,
                       constraints,
                       max_obs_duration=np.inf):
    """
    Identify observable follow-up windows.

    Parameters
    ----------
    tforecast : `~astropy.time.Time`
        The time array for the transit forecast.

    forecast : `~numpy.array`
        The transit forecast.

    target : `~astroplan.FixedTarget`
        A target object.

    site : `~astroplan.Observer`
        A site object.

    constraints : iterable
        A list of `~astroplan.Constraint` objects.

    max_obs_duration : float or `~astropy.units.Quantity`, optional
        The maximum duration of an observation. Defaults to days if unit not
        specified.

    Returns
    -------
    windows : `~pandas.DataFrame`
        A table of the observable windows.
    """
    if not isinstance(tforecast, Time):
        raise TypeError(
            'The times must be specified with an astropy.time.Time instance.')

    if isinstance(max_obs_duration, units.Quantity):
        max_obs_duration = max_obs_duration.to(units.day).value

    # For simplicity, just use BJD times
    times = tforecast.jd

    observability = ap.is_event_observable(constraints,
                                           site,
                                           target,
                                           times=tforecast).flatten()

    idx_observable = np.where(observability)[0]
    idx_window_list = np.split(idx_observable,
                               np.where(np.diff(idx_observable) != 1)[0] + 1)
    dts = []
    t_starts = []
    t_ends = []
    t_maxs = []
    Ms = []
    for idx_window in idx_window_list:
        t_max = (times[idx_window])[np.argmax(forecast[idx_window])]
        idx_window = _refine_window(times, t_max, idx_window, max_obs_duration)
        t_start = (times[idx_window]).min()
        t_end = (times[idx_window]).max()
        dt = (times[idx_window]).ptp()
        M = np.trapz(forecast[idx_window], times[idx_window]) / dt

        dts.append(dt)
        t_starts.append(t_start)
        t_ends.append(t_end)
        t_maxs.append(t_max)
        Ms.append(M)

    windows = pd.DataFrame({
        't_start': t_starts,
        't_max': t_maxs,
        't_end': t_ends,
        'dt': dts,
        'M': Ms
    }).sort_values('M', ascending=False)

    return windows
Beispiel #11
0
    def __init__(self,
                 t0=None,
                 period=None,
                 perr=None,
                 duration=None,
                 loc='Siding Spring Observatory',
                 timezone='Australia/NSW',
                 ra=None,
                 dec=None,
                 startdate=None,
                 starttime='0:00',
                 run_length=180,
                 el_limit=30,
                 toi=None):

        if toi is not None:
            self.set_params_from_toi(toi)

        else:

            self.epoch = Time(t0 + 2457000, format='jd')
            self.period = period * u.d
            if perr is not None:
                self.period_err = perr * u.d
            else:
                self.period_err = None

            self.duration = duration / 24 * u.d
            self.coords = [ra * u.deg, dec * u.deg]

        self.alt_limit = el_limit

        self.observatory = loc
        self.timezone = timezone

        self.obs_mid_times_utc = None
        self.obs_mid_uncerts = None
        self.obs_mid_times_local = None

        coord = SkyCoord(ra=self.coords[0], dec=self.coords[1])
        target = FixedTarget(coord, name='Target')

        SSO = Observer.at_site(self.observatory, timezone=self.timezone)

        planet = EclipsingSystem(primary_eclipse_time=self.epoch,
                                 orbital_period=self.period,
                                 duration=self.duration)

        starttime = startdate + ' ' + starttime
        self.start = Time(starttime)
        self.run_length = run_length * u.d

        n_transits = np.ceil(self.run_length / self.period)

        self.all_transit_times = planet.next_primary_eclipse_time(
            self.start, n_eclipses=n_transits)

        diff = self.all_transit_times - self.start - self.run_length
        real_trans = diff.value < 0
        if np.sum(real_trans) < 0.5:
            self.all_transit_times = None
            return
        else:
            self.all_transit_times = self.all_transit_times[real_trans]

        if self.period_err is not None:
            self.uncert_vals = (self.all_transit_times - self.epoch
                                ) * self.period_err / self.period * 1440

        constraints = [
            AtNightConstraint.twilight_nautical(),
            AltitudeConstraint(min=self.alt_limit * u.deg)
        ]

        obs_mid = is_event_observable(constraints,
                                      SSO,
                                      target,
                                      times=self.all_transit_times)[0]
        obs_start = is_event_observable(constraints,
                                        SSO,
                                        target,
                                        times=self.all_transit_times -
                                        0.5 * self.duration)[0]
        obs_end = is_event_observable(constraints,
                                      SSO,
                                      target,
                                      times=self.all_transit_times +
                                      0.5 * self.duration)[0]

        if np.sum(obs_mid * obs_start * obs_end) > 0:

            self.obs_airmass = np.zeros(
                (np.sum(obs_mid * obs_start * obs_end), 3))

            self.obs_mid_times_utc = self.all_transit_times[obs_mid *
                                                            obs_start *
                                                            obs_end]
            if self.period_err is not None:
                self.obs_mid_uncerts = self.uncert_vals[obs_mid * obs_start *
                                                        obs_end]

            for i in range(np.sum(obs_mid * obs_start * obs_end)):
                obs_airmass_mid = SSO.altaz(self.obs_mid_times_utc[i],
                                            coord).secz
                obs_airmass_start = SSO.altaz(
                    self.obs_mid_times_utc[i] - self.duration / 2, coord).secz
                obs_airmass_end = SSO.altaz(
                    self.obs_mid_times_utc[i] + self.duration / 2, coord).secz

                self.obs_airmass[i] = [
                    obs_airmass_start, obs_airmass_mid, obs_airmass_end
                ]

            # NEED A BARYCENTRIC CORRECTION

            tz = pytz.timezone('utc')

            dtime = self.obs_mid_times_utc.to_datetime()

            self.obs_mid_times_local = np.array([])

            for i in range(len(self.obs_mid_times_utc)):
                inoz = tz.localize(dtime[i])
                if self.period_err is not None:
                    indiv_uncert = self.obs_mid_uncerts[i]
                val = inoz.astimezone(pytz.timezone(self.timezone))
                self.obs_mid_times_local = np.append(
                    self.obs_mid_times_local,
                    val.strftime('%Y-%m-%d %H:%M:%S'))
    def Observability(self, obs_time, Nights, constraints): #check_eclipse, check_target=0, delta_midnight=None
        """
            Calculates if the Transit and the target are observable for each date during the given timespan in ''Nights'' under
            the given ''constraints'' and writes it as dict objects into ''~self.eclipse_observable'' or ''~self.target_observable''.

            Parameters
            ----------
            obs_time : astropy.time.Time
                Contains the datetime as Time format after which the possible observations should be found.

            Nights : class Nights
                Containing night data of paranal, see Nights documentation.

            constraints : class astroplan.Constraint
                Constraints under which the observational events should get constrained.

            check_eclipse : int
                If ''check_eclipse'' =  1, checks if transits/eclipses are observable.

            check_target : int, optional
                If ''check_target'' = 1, checks if target is observable during the given nights. The default is 0.

            delta_midnight : numpy.linspace, obtional
                array containing a grid of timesteps for which the nights datetimes should get computed. Default is None

        """
        Planet_next_eclipse_Times = self.Planets_eclipse.next_primary_eclipse_time(
            obs_time, n_eclipses=self.num_eclipses)
        print(self.name + ' is getting processed')
        # n_max = np.ceil(1/self.period_err)
        # for date in Nights.date:

        #     if check_target == 1:
        #         """ Check if Nights object has attribute nights to calculate observability of target """
        #         if hasattr(Nights, 'night'):
        #             k = list.index(Nights.date, date)
        #             night = Nights.night[k]
        #         else:
        #             Nights.Calculate_nights_paranal(delta_midnight)
        #             night = Nights.night[0]
        Planet_Eclipes_NIGHTs_All = []
        for n, planet_next_eclipse_by_date in enumerate(Planet_next_eclipse_Times):
            """ Loop over all eclipses coming up in the given timespan of object planet """
            
            """ 
                Barycentric light travel time correction, since mid transit times are in barycentric frame. 
                Need to transform time to geocentric frame: 
            """
            planet_next_eclipse_by_date.location = paranal.location
            ltt_bary = planet_next_eclipse_by_date.light_travel_time(self.Coordinates.coord)
            planet_next_eclipse_by_date = planet_next_eclipse_by_date - ltt_bary # barycentric correction, 
            # the minus comes from that we transform from the barycentric into the geocentric frame.
            
            # Check which eclipse can be observed in which night
            # if date == planet_next_eclipse_by_date.datetime.date():

            # if check_eclipse == 1:

            Planet_next_eclipse_per_night_MID = planet_next_eclipse_by_date
            Planet_next_eclipse_per_night_BEGIN = Planet_next_eclipse_per_night_MID - \
                self.transit_duration / 2
            Planet_next_eclipse_per_night_END = Planet_next_eclipse_per_night_MID + \
                self.transit_duration / 2
            
            Planet_Eclipse_ERROR = (np.sqrt(self.tranmid_err.sec**2+(n+1)**2*self.period_err.to_value(u.second)**2)*u.second).to(u.hour)
            Planet_Eclipes_NIGHT = [Planet_next_eclipse_per_night_BEGIN, Planet_next_eclipse_per_night_MID,
                                    Planet_next_eclipse_per_night_END]  # Begin, midpoint and end of transit

            # Planet_Eclipes_NIGHTs_All = Planet_Eclipes_NIGHTs_All + Planet_Eclipes_NIGHT
            
            """ Computes observability of the Transit """
            ecl_obs = astroplan.is_event_observable(
                constraints=constraints, observer=paranal, target=self.Coordinates, times=Planet_Eclipes_NIGHT)
            
            # ecl_obs_test = astroplan.is_event_observable(
            #     constraints=constraints, observer=paranal, target=self.Coordinates, times=Planet_Eclipes_NIGHTs_All)
            
            # for k in range(int(len(ecl_obs_test[0])/3)):
            #     flag_not_obs = False
            #     for n in range(3):
            #         if ecl_obs_test[0][n+k] == False:
            #             flag_not_obs = True
            #     if not flag_not_obs:
            #         Planet_Eclipes_NIGHT_obs = [Planet_Eclipes_NIGHTs_All[0+k], Planet_Eclipes_NIGHTs_All[1+k], Planet_Eclipes_NIGHTs_All[2+k]]
                
            if all(ecl_obs[0] == True):
                print('{} total Eclipse is observable'.format(self.name))
                airmass_moon_sep_obj_altaz_RESULT = [
                    fun.airmass_moon_sep_obj_altaz(self, tim) for tim in Planet_Eclipes_NIGHT] #HERE Planet_Eclipes_NIGHT_obs
                moon_target_sep = [
                    out[0] for out in airmass_moon_sep_obj_altaz_RESULT]
                moon_phase = [out[1]
                              for out in airmass_moon_sep_obj_altaz_RESULT]
                airmass = [out[2]
                           for out in airmass_moon_sep_obj_altaz_RESULT]
                obs_altazs = [out[3]
                              for out in airmass_moon_sep_obj_altaz_RESULT]
                # print(moon_target_sep, moon_phase, airmass, obs_altazs)
                self.eclipse_observable.append({
                    'Name': self.name,
                    'Radius R_J': self.pl_radj,
                    'Transit Midpoint Time': Planet_next_eclipse_per_night_MID, #Planet_Eclipes_NIGHT_obs[1],
                    'Transit Midpoint Time uncertainty [h]': Planet_Eclipse_ERROR,
                    'Primary eclipse observable?': ecl_obs[0][0],
                    'Transit Duration [h]': self.transit_duration.to(u.hour),
                    'Effective Temperature K': self.star_Teff,
                    'J-magnitude': self.star_jmag,
                    'Eclipse Begin': {'time': Planet_next_eclipse_per_night_BEGIN, #Planet_Eclipes_NIGHT_obs[0] ,
                                      'airmass': airmass[0],
                                      'moon sep': moon_target_sep[0][0],
                                      'moon phase': moon_phase[0],
                                      'az': obs_altazs[0].az,
                                      'alt': obs_altazs[0].alt
                                      },
                    'Eclipse Mid': {'time': Planet_next_eclipse_per_night_MID, #Planet_Eclipes_NIGHT_obs[1] ,
                                    'airmass': airmass[1],
                                    'moon sep': moon_target_sep[1][0],
                                    'moon phase': moon_phase[1],
                                    'az': obs_altazs[1].az,
                                    'alt': obs_altazs[1].alt
                                    },
                    'Eclipse End': {'time': Planet_next_eclipse_per_night_END, #Planet_Eclipes_NIGHT_obs[2],
                                    'airmass': airmass[2],
                                    'moon sep': moon_target_sep[2][0],
                                    'moon phase': moon_phase[2],
                                    'az': obs_altazs[2].az,
                                    'alt': obs_altazs[2].alt
                                    }})
Beispiel #13
0
def create_observation_observables(object_id,
                                   object_dir,
                                   since,
                                   name,
                                   epoch,
                                   epoch_low_err,
                                   epoch_up_err,
                                   period,
                                   period_low_err,
                                   period_up_err,
                                   duration,
                                   observatories_file,
                                   timezone,
                                   latitude,
                                   longitude,
                                   altitude,
                                   max_days,
                                   min_altitude,
                                   moon_min_dist,
                                   moon_max_dist,
                                   transit_fraction,
                                   baseline,
                                   error_alert=True):
    """

    @param object_id: the candidate id
    @param object_dir: the candidate directory
    @param since: starting plan date
    @param name: the name given to the candidate
    @param epoch: the candidate epoch
    @param epoch_low_err: the candidate epoch's lower error
    @param epoch_up_err: the candidate epoch's upper error
    @param period: the candidate period
    @param period_low_err: the candidate period's lower error
    @param period_up_err: the candidate period's upper error
    @param duration: the candidate duration
    @param observatories_file: the file containing the observatories file (csv format)
    @param timezone: the timezone of the observatory (if observatories_file=None)
    @param latitude: the latitude of the observatory (if observatories_file=None)
    @param longitude: the longitude of the observatory (if observatories_file=None)
    @param altitude: the altitude of the observatory (if observatories_file=None)
    @param max_days: the maximum number of days to compute the observables
    @param min_altitude: the minimum altitude of the target above the horizon
    @param moon_min_dist: the minimum moon distance for moon illumination = 0
    @param moon_max_dist: the minimum moon distance for moon illumination = 1
    @param transit_fraction: the minimum transit observability (0.25 for at least ingress/egress, 0.5 for ingress/egress
    + midtime, 1 for ingress, egress and midtime).
    @param baseline: the required baseline in hours.
    @param: error_alert: whether to create the alert date to signal imprecise observations
    @return: the generated data and target folders
    """
    if observatories_file is not None:
        observatories_df = pd.read_csv(observatories_file, comment='#')
    else:
        observatories_df = pd.DataFrame(
            columns=['name', 'tz', 'lat', 'long', 'alt'])
        observatories_df = observatories_df.append("Obs-1", timezone, latitude,
                                                   longitude, altitude)
    # TODO probably convert epoch to proper JD
    mission, mission_prefix, id_int = LcBuilder().parse_object_info(object_id)
    if mission == "TESS":
        primary_eclipse_time = Time(epoch, format='btjd', scale="tdb")
    elif mission == "Kepler" or mission == "K2":
        primary_eclipse_time = Time(epoch, format='bkjd', scale="tdb")
    else:
        primary_eclipse_time = Time(epoch, format='jd')
    target = FixedTarget(SkyCoord(coords, unit=(u.deg, u.deg)))
    n_transits = int(max_days // period)
    system = EclipsingSystem(primary_eclipse_time=primary_eclipse_time,
                             orbital_period=u.Quantity(period, unit="d"),
                             duration=u.Quantity(duration, unit="h"),
                             name=name)
    observables_df = pd.DataFrame(columns=[
        'observatory', 'timezone', 'start_obs', 'end_obs', 'ingress', 'egress',
        'midtime', "midtime_up_err_h", "midtime_low_err_h", 'twilight_evening',
        'twilight_morning', 'observable', 'moon_phase', 'moon_dist'
    ])
    plan_dir = object_dir + "/plan"
    images_dir = plan_dir + "/images"
    if os.path.exists(plan_dir):
        shutil.rmtree(plan_dir, ignore_errors=True)
    os.mkdir(plan_dir)
    if os.path.exists(images_dir):
        shutil.rmtree(images_dir, ignore_errors=True)
    os.mkdir(images_dir)
    alert_date = None
    for index, observatory_row in observatories_df.iterrows():
        observer_site = Observer(latitude=observatory_row["lat"],
                                 longitude=observatory_row["lon"],
                                 elevation=u.Quantity(observatory_row["alt"],
                                                      unit="m"))
        midtransit_times = system.next_primary_eclipse_time(
            since, n_eclipses=n_transits)
        ingress_egress_times = system.next_primary_ingress_egress_time(
            since, n_eclipses=n_transits)
        constraints = [
            AtNightConstraint.twilight_nautical(),
            AltitudeConstraint(min=min_altitude * u.deg),
            MoonIlluminationSeparationConstraint(
                min_dist=moon_min_dist * u.deg, max_dist=moon_max_dist * u.deg)
        ]
        moon_for_midtransit_times = get_moon(midtransit_times)
        moon_dist_midtransit_times = moon_for_midtransit_times.separation(
            SkyCoord(star_df.iloc[0]["ra"], star_df.iloc[0]["dec"],
                     unit="deg"))
        moon_phase_midtransit_times = np.round(
            astroplan.moon_illumination(midtransit_times), 2)
        transits_since_epoch = np.round(
            (midtransit_times - primary_eclipse_time).jd / period)
        midtransit_time_low_err = np.round(
            (((transits_since_epoch * period_low_err)**2 + epoch_low_err**2)
             **(1 / 2)) * 24, 2)
        midtransit_time_up_err = np.round(
            (((transits_since_epoch * period_up_err)**2 + epoch_up_err**2)
             **(1 / 2)) * 24, 2)
        low_err_delta = TimeDelta(midtransit_time_low_err * 3600, format='sec')
        up_err_delta = TimeDelta(midtransit_time_up_err * 3600, format='sec')
        i = 0
        for midtransit_time in midtransit_times:
            twilight_evening = observer_site.twilight_evening_nautical(
                midtransit_time)
            twilight_morning = observer_site.twilight_morning_nautical(
                midtransit_time)
            ingress = ingress_egress_times[i][0]
            egress = ingress_egress_times[i][1]
            lowest_ingress = ingress - low_err_delta[i]
            highest_egress = egress + up_err_delta[i]
            if error_alert and (highest_egress - lowest_ingress).jd > 0.33:
                alert_date = midtransit_time if (alert_date is None) or (
                    alert_date is not None
                    and alert_date >= midtransit_time) else alert_date
                break
            else:
                baseline_low = lowest_ingress - baseline * u.hour
                baseline_up = highest_egress + baseline * u.hour
                transit_times = baseline_low + (
                    baseline_up - baseline_low) * np.linspace(0, 1, 100)
                observable_transit_times = astroplan.is_event_observable(
                    constraints, observer_site, target, times=transit_times)[0]
                observable_transit_times_true = np.argwhere(
                    observable_transit_times)
                observable = len(observable_transit_times_true) / 100
                if observable < transit_fraction:
                    i = i + 1
                    continue
                start_obs = transit_times[observable_transit_times_true[0]][0]
                end_obs = transit_times[observable_transit_times_true[
                    len(observable_transit_times_true) - 1]][0]
                start_plot = baseline_low
                end_plot = baseline_up
                if twilight_evening > start_obs:
                    start_obs = twilight_evening
                if twilight_morning < end_obs:
                    end_obs = twilight_morning
            moon_dist = round(moon_dist_midtransit_times[i].degree)
            moon_phase = moon_phase_midtransit_times[i]
            # TODO get is_event_observable for several parts of the transit (ideally each 5 mins) to get the proper observable percent. Also with baseline
            if observatory_row["tz"] is not None and not np.isnan(
                    observatory_row["tz"]):
                observer_timezone = observatory_row["tz"]
            else:
                observer_timezone = get_offset(observatory_row["lat"],
                                               observatory_row["lon"],
                                               midtransit_time.datetime)
            observables_df = observables_df.append(
                {
                    "observatory":
                    observatory_row["name"],
                    "timezone":
                    observer_timezone,
                    "ingress":
                    ingress.isot,
                    "start_obs":
                    start_obs.isot,
                    "end_obs":
                    end_obs.isot,
                    "egress":
                    egress.isot,
                    "midtime":
                    midtransit_time.isot,
                    "midtime_up_err_h":
                    str(int(midtransit_time_up_err[i] // 1)) + ":" +
                    str(int(midtransit_time_up_err[i] % 1 * 60)).zfill(2),
                    "midtime_low_err_h":
                    str(int(midtransit_time_low_err[i] // 1)) + ":" +
                    str(int(midtransit_time_low_err[i] % 1 * 60)).zfill(2),
                    "twilight_evening":
                    twilight_evening.isot,
                    "twilight_morning":
                    twilight_morning.isot,
                    "observable":
                    observable,
                    "moon_phase":
                    moon_phase,
                    "moon_dist":
                    moon_dist
                },
                ignore_index=True)
            plot_time = start_plot + (end_plot - start_plot) * np.linspace(
                0, 1, 100)
            plt.tick_params(labelsize=6)
            airmass_ax = plot_airmass(target,
                                      observer_site,
                                      plot_time,
                                      brightness_shading=False,
                                      altitude_yaxis=True)
            airmass_ax.axvspan(twilight_morning.plot_date,
                               end_plot.plot_date,
                               color='white')
            airmass_ax.axvspan(start_plot.plot_date,
                               twilight_evening.plot_date,
                               color='white')
            airmass_ax.axvspan(twilight_evening.plot_date,
                               twilight_morning.plot_date,
                               color='gray')
            airmass_ax.axhspan(1. / np.cos(np.radians(90 - min_altitude)),
                               5.0,
                               color='green')
            airmass_ax.get_figure().gca().set_title("")
            airmass_ax.get_figure().gca().set_xlabel("")
            airmass_ax.get_figure().gca().set_ylabel("")
            airmass_ax.set_xlabel("")
            airmass_ax.set_ylabel("")
            xticks = []
            xticks_labels = []
            xticks.append(start_obs.plot_date)
            hour_min_sec_arr = start_obs.isot.split("T")[1].split(":")
            xticks_labels.append("T1_" + hour_min_sec_arr[0] + ":" +
                                 hour_min_sec_arr[1])
            plt.axvline(x=start_obs.plot_date, color="violet")
            xticks.append(end_obs.plot_date)
            hour_min_sec_arr = end_obs.isot.split("T")[1].split(":")
            xticks_labels.append("T1_" + hour_min_sec_arr[0] + ":" +
                                 hour_min_sec_arr[1])
            plt.axvline(x=end_obs.plot_date, color="violet")
            if start_plot < lowest_ingress < end_plot:
                xticks.append(lowest_ingress.plot_date)
                hour_min_sec_arr = lowest_ingress.isot.split("T")[1].split(":")
                xticks_labels.append("T1_" + hour_min_sec_arr[0] + ":" +
                                     hour_min_sec_arr[1])
                plt.axvline(x=lowest_ingress.plot_date, color="red")
            if start_plot < ingress < end_plot:
                xticks.append(ingress.plot_date)
                hour_min_sec_arr = ingress.isot.split("T")[1].split(":")
                xticks_labels.append("T1_" + hour_min_sec_arr[0] + ":" +
                                     hour_min_sec_arr[1])
                plt.axvline(x=ingress.plot_date, color="orange")
            if start_plot < midtransit_time < end_plot:
                xticks.append(midtransit_time.plot_date)
                hour_min_sec_arr = midtransit_time.isot.split("T")[1].split(
                    ":")
                xticks_labels.append("T0_" + hour_min_sec_arr[0] + ":" +
                                     hour_min_sec_arr[1])
                plt.axvline(x=midtransit_time.plot_date, color="black")
            if start_plot < egress < end_plot:
                xticks.append(egress.plot_date)
                hour_min_sec_arr = egress.isot.split("T")[1].split(":")
                xticks_labels.append("T4_" + hour_min_sec_arr[0] + ":" +
                                     hour_min_sec_arr[1])
                plt.axvline(x=egress.plot_date, color="orange")
            if start_plot < highest_egress < end_plot:
                xticks.append(highest_egress.plot_date)
                hour_min_sec_arr = highest_egress.isot.split("T")[1].split(":")
                xticks_labels.append("T4_" + hour_min_sec_arr[0] + ":" +
                                     hour_min_sec_arr[1])
                plt.axvline(x=highest_egress.plot_date, color="red")
            airmass_ax.xaxis.set_tick_params(labelsize=5)
            airmass_ax.set_xticks([])
            airmass_ax.set_xticklabels([])
            degrees_ax = get_twin(airmass_ax)
            degrees_ax.yaxis.set_tick_params(labelsize=6)
            degrees_ax.set_yticks([1., 1.55572383, 2.])
            degrees_ax.set_yticklabels([90, 50, 30])
            fig = matplotlib.pyplot.gcf()
            fig.set_size_inches(1.25, 0.75)
            plt.savefig(plan_dir + "/images/" + observatory_row["name"] + "_" +
                        str(midtransit_time.isot)[:-4] + ".png",
                        bbox_inches='tight')
            plt.close()
            i = i + 1
    observables_df = observables_df.sort_values(["midtime", "observatory"],
                                                ascending=True)
    observables_df.to_csv(plan_dir + "/observation_plan.csv", index=False)
    print("Observation plan created in directory: " + object_dir)
    return observatories_df, observables_df, alert_date, plan_dir, images_dir
Beispiel #14
0
n_transits = 169  # The number of transits per year
observing_time = Time('2018-08-01 12:00')

utc_minus_six_hour = TimezoneInfo(utc_offset=-6 * u.hour)

constraints = [
    AtNightConstraint.twilight_astronomical(),
    AltitudeConstraint(min=30 * u.deg),
]

ing_egr = HATP32b.next_primary_ingress_egress_time(observing_time,
                                                   n_eclipses=n_transits)

hits = is_event_observable(constraints,
                           observer,
                           HATP32,
                           times_ingress_egress=ing_egr)

# Create a list of ingress-egress time objects matching constraints.
candidates = []
for i in range(len(hits[0])):
    if hits[0][i] == True:
        candidates.append(ing_egr[i])

# Convert from Julian to ISO
for i in candidates:
    i.format = 'iso'

f = open('observation_times.txt', 'w')
f.write("Obervation times [Ingress Egress] --- UTC (MST is UTC - 6)\n")
for i in candidates:
    def predict(self):

        #         # Make sure baseline has units
        #         if baseline is not None:
        #             try:
        #                 baseline.unit
        #             except AttributeError:
        #                 warn("No units specified for input 'baseline'."
        #                      +" Assuming hours.")
        #                 baseline = baseline * u.h

        # Inputs from object's attributes
        t1, t2 = self.meta['Time_limits']
        info = self.info
        n_eclipses = 500
        constraints_list = self.constraints
        obs = self.obs
        supp_cols = self.supp_cols

        # Define needed quantities based on planets infos
        # Must be quatities arrays (astropy)
        # Here we use a given astropy Table (info) to get the infos

        epoch, period, transit_duration =   \
            [info[k_col].quantity for k_col
             in ('pl_tranmid', 'pl_orbper', 'pl_trandur')]
        epoch = Time(epoch, format='jd')
        pl_name = info['pl_name']

        observing_time = t1

        # Init output table
        col_names = (
            'pl_name',
            'mid_tr',
            'AM_mid_tr',
            'tr_start',
            'tr_end',
            'AM_tr_start',
            'AM_tr_end',
            #                      'start',
            #                      'end',
            #                      'AM_start',
            #                      'AM_end',
            'Obs_start',
            'Baseline_before',
            'Obs_end',
            'Baseline_after',
            'moon',
            *supp_cols)
        description = {
            'mid_tr': "Time at mid transit [UTC]",
            'AM_mid_tr': "Airmass at mid transit",
            'tr_start': "Time at first contact t1 [UTC]",
            'tr_end': "Time at last contact t4 [UTC]",
            'AM_tr_start': "Airmass at first contact t1 [UTC]",
            'AM_tr_end': "Airmass at last contact t4 [UTC]",
            'Obs_start':
            "Beginning of target observability according to input constraints [UTC]",
            'Baseline_before': "'tr_start' - 'Obs_start'. Can be negative.",
            'Obs_end':
            "End of target observability according to input constraints [UTC]",
            'Baseline_after': "'Obs_end' - 'tr_end'. Can be negative."
        }
        meta = {**self.meta}
        full_table = Table()

        # Iterations on the targets
        for itar, target in enumerate(self.targets):

            # -------------------------
            # Steps to predict transits
            # -------------------------

            # Define system
            sys = EclipsingSystem(primary_eclipse_time=epoch[itar],
                                  orbital_period=period[itar],
                                  duration=transit_duration[itar],
                                  name=target.name)

            # Find all events ...
            while True:
                t_mid = sys.next_primary_eclipse_time(observing_time,
                                                      n_eclipses=n_eclipses)
                # ... until t2 is passed
                if t_mid[-1] > t2:
                    break
                    # or add eclipses to pass t2
                else:
                    n_eclipse += 500

            # Remove events after time window
            t_mid = t_mid[t_mid < t2]

            # Number of events
            n_event, = t_mid.shape

            # Get ingress and egress times
            t1_t4 = sys.next_primary_ingress_egress_time(observing_time,
                                                         n_eclipses=n_event)

            # Which mid transit times are observable
            i_mid = is_event_observable(constraints_list,
                                        obs,
                                        target,
                                        times=t_mid).squeeze()

            # Which ingress are observable ...
            i_t1 = is_event_observable(constraints_list,
                                       obs,
                                       target,
                                       times=t1_t4[:, 0]).squeeze()
            # ... when mid transit is not.
            i_t1 = i_t1 & ~i_mid

            # Which egress are observable ...
            i_t4 = is_event_observable(constraints_list,
                                       obs,
                                       target,
                                       times=t1_t4[:, 1]).squeeze()
            # ... when mid transit and ingress is not.
            i_t4 = i_t4 & ~i_mid & ~i_t1

            # Keep events where ingress, mid_transit or egress is observable
            index = i_mid | i_t1 | i_t4

            # Get observability for these events.
            # Starting point to compute the observability
            t_obs = np.concatenate(
                [t_mid[i_mid], t1_t4[i_t1, 0], t1_t4[i_t4, 1]])
            t_obs = Time(t_obs).sort()

            # Get observability range for each of these events
            obs_start = min_start_times(constraints_list, obs, target, t_obs)
            obs_end = max_end_times(constraints_list, obs, target, t_obs)

            # -------------------
            # End of steps to predict transits
            # -------------------

            # Put the infos in a table and stack it to the full table
            if index.any():
                name = np.repeat(sys.name, index.sum()).astype(str)
                moon = obs.moon_illumination(t_mid[index])
                AM_mid = obs.altaz(t_mid[index], target).secz
                AM_t1_t4 = obs.altaz(t1_t4[index], target).secz
                #         AM_base = obs.altaz(t_baseline[index], target).secz
                #                 obs_start = min_start_times(constraints_list, obs, target, t_mid[index])
                baseline_before = (t1_t4[index, 0] - obs_start).to('min')
                #                 obs_end = max_end_times(constraints_list, obs, target, t_mid[index])
                baseline_after = (obs_end - t1_t4[index, 1]).to('min')
                supp = [
                    np.repeat(info[key][itar], index.sum())
                    for key in supp_cols
                ]
                cols = [
                    name,
                    t_mid[index].iso,
                    AM_mid,
                    t1_t4[index, 0].iso,
                    t1_t4[index, 1].iso,
                    AM_t1_t4[:, 0],
                    AM_t1_t4[:, 1],
                    #                         *t_baseline[index].T.iso,
                    #                         *AM_base.T,
                    obs_start.iso,
                    baseline_before,
                    obs_end.iso,
                    baseline_after,
                    moon,
                    *supp
                ]
                table_sys = Table(cols, names=col_names, masked=True)
                full_table = vstack([table_sys, full_table])
            else:
                warnings.warn('No event found for ' + sys.name,
                              AstropyUserWarning)

        if full_table:
            full_table.sort('mid_tr')
            full_table.meta = meta
        else:
            warnings.warn('No event found at all', AstropyUserWarning)

        # Add column descriptions
        for col in full_table.colnames:
            try:
                full_table[col].description = description[col]
            except KeyError:
                pass

        return full_table
    def predict(self,
                phase_range,
                obs_time=3. * u.h,
                dt_grid=0.5 * u.h,
                phase_constraint=True):
        '''
        Parameters:
        -----------
        phase_range: list, len=2
            Phase range
        obs_time: quantity or float
            minimum required observing time
        dt_grid: quantity or float
            grid steps used to compute observability between phase range.
        '''
        if not hasattr(obs_time, 'unit'):
            obs_time = obs_time * u.h
        if not hasattr(dt_grid, 'unit'):
            dt_grid = dt_grid * u.h

        t1, t2 = self.meta['Time_limits']
        info = self.info
        n_eclipses = 500  # TODO: COuld be computed according to period and time range
        constraints_list = self.constraints
        obs = self.obs
        supp_cols = self.supp_cols

        # Define needed quantities based on planets infos
        # Must be quatities arrays (astropy)
        # Here we use a given astropy Table (info) to get the infos

        epoch, period = [
            info[k_col].quantity for k_col in ('pl_phase_zero', 'pl_orbper')
        ]
        epoch = Time(epoch, format='jd')
        pl_name = info['pl_name']

        window_start = t1

        # Init output table
        col_names = ('pl_name', 'Obs_start', 'Phase_start', 'Obs_end',
                     'Phase_end', 'mid_phase', 'AM_mid_phase', 'observability',
                     'moon', *supp_cols)

        meta = {'Phase_range': phase_range, **self.meta}
        full_table = Table()

        # Iterations on the targets
        for itar, target in enumerate(self.targets):

            # -------------------------
            # Steps to predict transits
            # -------------------------

            d_phase = (dt_grid / period[itar]).decompose().value
            [p1, p2] = phase_range
            p1 += d_phase / 10  # Make sure it's not on the boundary
            p2 -= d_phase / 10
            phase_grid = np.arange(p1, p2, d_phase)
            phase_grid = phase_grid * period[itar] + epoch[itar]

            while True:
                # Find all events for each phase point in grid ...
                t_grid = []
                for phase in phase_grid:
                    # Define a system for each phase point
                    sys = EclipsingSystem(primary_eclipse_time=phase,
                                          orbital_period=period[itar])
                    # Compute all events and save
                    t_temp = sys.next_primary_eclipse_time(
                        window_start, n_eclipses=n_eclipses)
                    t_grid.append(t_temp.jd)
                # Convert to Time object
                t_grid = Time(t_grid, format='jd').T

                # Do so until t2 is passed
                if t_grid[-1, -1] > t2:
                    break
                    # or add eclipses to pass t2 and recompute t_grid
                else:
                    n_eclipses += 500

            if (np.diff(t_grid.jd, axis=-1) <= 0).any():
                message = 'Time limit t1 falls into phase range.'  \
                        + ' This will be corrected eventually.'  \
                        + ' For now, please change the time limit t1.'
                raise ValueError(message)

            t_grid = t_grid[(t_grid < t2).any(axis=1)]

            events = []
            for grid in t_grid:
                index = is_event_observable(constraints_list,
                                            obs,
                                            target,
                                            times=grid).squeeze()
                if index.any():
                    events.append(np.mean(grid[index].jd))
            events = Time(events, format='jd')

            # Finally add phase constraint
            sys = PeriodicEvent(epoch=epoch[itar], period=period[itar])

            if phase_constraint:
                final_constraints = [
                    *constraints_list,
                    PhaseConstraint(sys, *phase_range)
                ]
            else:
                final_constraints = constraints_list

            # TODO: Add something to check the dt in min_start_times (can bug if dt_grid too small)
            obs_start = min_start_times(final_constraints, obs, target, events)
            obs_end = max_end_times(final_constraints, obs, target, events)
            baseline = obs_end - obs_start
            t_mid = obs_start + baseline / 2

            index = (obs_end - obs_start) > obs_time

            # -------------------
            # End of steps to predict events
            # -------------------

            # Put the infos in a table and stack it to the full table
            if index.any():
                name = np.repeat(target.name, index.sum()).astype(str)
                moon = obs.moon_illumination(t_mid[index])
                phase_start = sys.phase(obs_start[index])
                phase_end = sys.phase(obs_end[index])
                observability = obs_end[index] - obs_start[index]
                AM_mid = obs.altaz(t_mid[index], target).secz
                supp = [
                    np.repeat(info[key][itar], index.sum())
                    for key in supp_cols
                ]
                cols = [
                    name, obs_start[index].iso, phase_start,
                    obs_end[index].iso, phase_end, t_mid[index].iso, AM_mid,
                    observability.to(u.h), moon, *supp
                ]
                table_sys = Table(cols, names=col_names, masked=True)
                full_table = vstack([table_sys, full_table])
            else:
                warn('No event found for ' + target.name, AstropyUserWarning)

        if full_table:
            full_table.sort('mid_phase')
            full_table.meta = meta
        else:
            warn('No event found at all', AstropyUserWarning)

        return full_table