예제 #1
0
    def __init__(self,
                 time_range,
                 targets,
                 site='cfht',
                 constraints=None,
                 supp_cols=None):

        # Get infos from the MasterFile
        if isinstance(targets, list):
            info = load_from_masterfile(*targets)
            supp_cols = supp_cols or [
                'pl_orbper', 'st_j', 'st_h', 'ra', 'dec', 'pl_eqt', 'st_teff'
            ]
        else:
            info = targets.copy()
            if supp_cols is None:
                supp_cols = list(info.keys())
                supp_cols.remove('pl_name')

        # Default constraint
        if constraints is None:
            constraints = [
                AtNightConstraint.twilight_nautical(),
                AirmassConstraint(max=2.5)
            ]

        # Define time constraint and append it
        t1, t2 = Time(time_range)
        constraints.append(TimeConstraint(t1, t2))

        # Convert to List_of_constraints (useful to print and save)
        constraints_list = List_of_constraints(constraints)

        # Save infos
        self.info = info
        self.constraints = constraints_list
        self.meta = {
            'Time_limits': [t1, t2],
            'Target_list': info['pl_name'].tolist(),
            'Site': site,
            **constraints_list.show()
        }
        self.supp_cols = supp_cols
        self.info_cols = ['pl_name'] + supp_cols
        self.obs = Observer.at_site(site)
        #         self.n_eclipses = n_eclipses

        # Resolve targets
        self.targets = [self.resolve_target(i) for i in range(len(targets))]
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
    )
예제 #3
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'))
예제 #4
0
파일: staralt.py 프로젝트: japp/staralt
def observability_objects(data):
    """
    Test the observability of a list of objects for a single date

    Parameters
    ----------
    data : POST data format

    data = {
        'observatory' : 'OT',
        'altitude_lower_limit' : '30',
        'altitude_higher_limit' : '90',
        'objects' : [{
                'name' : 'Kelt 8b',
                'RA' : 283.30551667 ,
                'Dec' : 24.12738139,
                'dates' : [
                        ['2020-06-11 00:16:30', '2020-06-11 03:44:26'],
                        ['2020-06-14 06:07:56', '2020-06-14 09:35:53']
                    ]
                },
                {
                    'name' : 'TIC 123456789',
                    'RA' : 13.13055667 ,
                    'Dec' : 24.13912738,
                    'dates' : [
                        ['2020-06-11 23:59:59']
                    ]
                }
            ]
        }

    Returns
    -------
    observability : dict
        Dictionary with the observability and moon distance for all objects

        {'V0879 Cas' : {
                'observability' : 'True', 'moon_separation' : 30.4
            },
        'RU Scl' : {
            'observability' : 'True', 'moon_separation' : 10.8
            }
        }

    """

    import astropy.units as u
    from astroplan import FixedTarget
    from astroplan import (AltitudeConstraint, AtNightConstraint)
    from astroplan import is_observable, is_always_observable

    # Site location
    location = get_location(data['observatory'])

    # dict of observability for each target
    observabilities = {}

    if 'twilight_type' not in data.keys():
        data['twilight_type'] = 'astronomical'

    if data['twilight_type'] == 'civil':
        twilight_constraint = AtNightConstraint.twilight_civil()
    elif data['twilight_type'] == 'nautical':
        twilight_constraint = AtNightConstraint.twilight_nautical()
    else:
        twilight_constraint = AtNightConstraint.twilight_astronomical()

    # Observation constraints
    constraints = [
        AltitudeConstraint(
            float(data['altitude_lower_limit']) * u.deg,
            float(data['altitude_higher_limit']) * u.deg), twilight_constraint
    ]

    for target in data['objects']:

        coords = SkyCoord(ra=target['RA'] * u.deg, dec=target['Dec'] * u.deg)
        fixed_target = [FixedTarget(coord=coords, name=target['name'])]

        observabilities[target['name']] = []

        for date in target['dates']:

            # time range for transits
            # Always observable for time range
            if len(date) > 1:
                # If exoplanet transit, test observability always during transit,
                # if not, test observability *ever* during night
                time_range = Time([date[0], date[1]])

                # Are targets *always* observable in the time range?
                observable = is_always_observable(constraints,
                                                  location,
                                                  fixed_target,
                                                  time_range=time_range)

            # No time range, *ever* observabable during the night
            # Observability is test from sunset to sunrise
            # Default time resolution is 0.5h
            else:
                sunset = location.sun_set_time(Time(date[0]))
                sunrise = location.sun_rise_time(Time(date[0]), 'next')
                time_range = Time([sunset, sunrise])
                observable = is_observable(constraints,
                                           location,
                                           fixed_target,
                                           time_range=time_range)

            # Moon location for the observation date
            moon = location.moon_altaz(Time(date[0]))
            moon_separation = moon.separation(coords)

            observabilities[target['name']].append({
                'observable':
                str(observable[0]),
                'moon_separation':
                moon_separation.degree
            })

    return observabilities
예제 #5
0
파일: staralt.py 프로젝트: japp/staralt
def observability_dates(data):
    """
    Test the observability of a single objects for several nights

    If the first element of 'dates' contains a single date, then
    the observability is test as *ever* for the night. 
    If a time range is given, observability is test as *always* for the time range

    Parameters
    ----------
    data : POST data format

        # data for transiting planet, time range constrained
        data = {
            'name' : 'Kelt 8b',
            'RA' : 283.30551667 ,
            'Dec' : 24.12738139,
            'observatory' : 'OT',
            'altitude_lower_limit' : '30',
            'altitude_higher_limit' : '90',
            'twilight_type' : 'astronomical',
            'dates' : [
                    ['2020-06-11 00:16:30', '2020-06-11 03:44:26'],
                    ['2020-06-14 06:07:56', '2020-06-14 09:35:53']
                ]
            }
        
        # data for ordinary target, twilight constrained
        # single date list

        data = {
            'name' : 'KIC8012732',
            'RA' : 284.72949583 ,
            'Dec' : 43.86421667,
            'observatory' : 'OT',
            'altitude_lower_limit' : '30',
            'altitude_higher_limit' : '90',
            'dates' : [
                        ['2020-06-11 23:00:00']
                    ]
            }

    Returns
    -------
    observability : dict
        Dictionary with the observability and moon distance for all objects

        {'V0879 Cas' : {
                'observability' : 'True', 'moon_separation' : 30.4
            },
        'RU Scl' : {
                'observability' : 'True', 'moon_separation' : 10.8
            }
        }

    """

    import astropy.units as u
    from astroplan import FixedTarget
    from astroplan import (AltitudeConstraint, AtNightConstraint)
    from astroplan import is_observable, is_always_observable

    # Site location
    location = get_location(data['observatory'])

    coords = SkyCoord(ra=data['RA'] * u.deg, dec=data['Dec'] * u.deg)
    fixed_target = [FixedTarget(coord=coords, name=data['name'])]

    # List of dates of observability
    observabilities = []

    if 'twilight_type' not in data.keys():
        data['twilight_type'] = 'astronomical'

    if data['twilight_type'] == 'civil':
        twilight_constraint = AtNightConstraint.twilight_civil()
    elif data['twilight_type'] == 'nautical':
        twilight_constraint = AtNightConstraint.twilight_nautical()
    else:
        twilight_constraint = AtNightConstraint.twilight_astronomical()

    # Observation constraints
    constraints = [
        AltitudeConstraint(
            float(data['altitude_lower_limit']) * u.deg,
            float(data['altitude_higher_limit']) * u.deg), twilight_constraint
    ]

    for date in data['dates']:

        # time range for transits
        # Always observable for time range
        if len(data['dates'][0]) > 0:

            # If exoplanet transits, check for observability always during transit,
            # if not, check observability *ever* during night
            time_range = Time([date[0], date[1]])

            # Are targets *always* observable in the time range?
            observable = is_always_observable(constraints,
                                              location,
                                              fixed_target,
                                              time_range=time_range)

        # No time range, *ever* observabable during the night
        else:
            observable = is_observable(constraints,
                                       location,
                                       fixed_target,
                                       times=Time(date[0]))

        # Moon location for the observation date
        moon = location.moon_altaz(Time(date[0]))
        moon_separation = moon.separation(coords)

        observabilities.append({
            'observable': str(observable[0]),
            'moon_separation': moon_separation.degree
        })

    return observabilities
예제 #6
0
파일: staralt.py 프로젝트: japp/staralt
def observability(data):
    """
    Test the observability of a list of objects for a single date

    Parameters
    ----------
    data : POST data format

        Observatory, date, limits and objects
        data = {
            'observatory' : 'OT',
            'date' : '2020-06-11 00:16:30',
            'date_end' : '2020-06-11 03:44:26',
            'altitude_lower_limit' : '30',
            'altitude_higher_limit' : '90',
            'twilight_type' : 'astronomical',
            'objects' : [{
                    'name' : 'Kelt 8b',
                    'RA' : 283.30551667 ,
                    'Dec' : 24.12738139
                    },
                    (more objects...)
                ]
            }

    Returns
    -------
    observability : dict
        Dictionary with the observability and moon distance for all objects
        {
            'V0879 Cas' : {
                'observability' : 'True', 'moon_separation' : 30.4
            },
            'RU Scl' : {
                'observability' : 'True', 'moon_separation' : 10.8
            }
        }

    """

    import astropy.units as u
    from astroplan import FixedTarget
    from astroplan import (AltitudeConstraint, AtNightConstraint)
    from astroplan import is_observable, is_always_observable

    # Site location
    location = get_location(data['observatory'])

    time_range = Time([data['date'], data['date_end']])

    if 'twilight_type' not in data.keys():
        data['twilight_type'] = 'astronomical'

    if data['twilight_type'] == 'civil':
        twilight_constraint = AtNightConstraint.twilight_civil()
    elif data['twilight_type'] == 'nautical':
        twilight_constraint = AtNightConstraint.twilight_nautical()
    else:
        twilight_constraint = AtNightConstraint.twilight_astronomical()

    # Observation constraints
    constraints = [
        AltitudeConstraint(
            int(data['altitude_lower_limit']) * u.deg,
            int(data['altitude_higher_limit']) * u.deg), twilight_constraint
    ]

    # Dictionary with star name and observability (bool str)
    result = {}

    # Moon location for the observation date
    middle_observing_time = time_range[-1] - (time_range[-1] -
                                              time_range[0]) / 2
    moon = location.moon_altaz(middle_observing_time)

    for target in data['objects']:
        # Object coordinates
        coords = SkyCoord(ra=target['RA'] * u.deg, dec=target['Dec'] * u.deg)
        fixed_target = [FixedTarget(coord=coords, name=target['name'])]

        if 'transit' in target.keys():
            time_range = Time(
                [target['transit']['t_early'], target['transit']['t_late']])
            # Are targets *always* observable in the time range?
            observable = is_always_observable(constraints,
                                              location,
                                              fixed_target,
                                              time_range=time_range)
        else:
            time_range = Time([data['date'], data['date_end']])
            # Are targets *ever* observable in the time range?
            observable = is_observable(constraints,
                                       location,
                                       fixed_target,
                                       time_range=time_range)

        moon_separation = moon.separation(coords)

        result[target['name']] = {
            'observable': str(observable[0]),
            'moon_separation': moon_separation.degree
        }

    return result
예제 #7
0
params.w = 90
params.ecc = 0
params.a = float(((G * M_star * (params.per * u.day)**2) /
                  (4 * np.pi**2))**(1 / 3) / R_star)

from astroplan import time_grid_from_range, observability_table

n_objects_per_night = int(sys.argv[-1])
print(n_objects_per_night)
airmass_cutoff = 3.5
fraction_cloudy = 0.3
n_years = 1
n_trials = 15

constraints = [
    AtNightConstraint.twilight_nautical(),
    AirmassConstraint(max=airmass_cutoff)
]

start_time = Time('2020-01-01 08:00')  # near local midnight
end_time = Time('2021-01-01 08:00')  # near local midnight

n_transits = []

for trial in range(n_trials):
    target_inds_observed = set([])

    obs_database = {
        name: dict(times=[], fluxes=[], model=[], transit=False)
        for name in table['spc']
    }
예제 #8
0
파일: plan.py 프로젝트: franpoz/SHERLOCK
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
예제 #9
0
    async def _schedule(self):
        """Actually do the scheduling, usually run in a separate process."""

        # only global constraint is the night
        if self._twilight == "astronomical":
            constraints = [AtNightConstraint.twilight_astronomical()]
        elif self._twilight == "nautical":
            constraints = [AtNightConstraint.twilight_nautical()]
        else:
            raise ValueError("Unknown twilight type.")

        # make shallow copies of all blocks and loop them
        copied_blocks = [copy.copy(block) for block in self._blocks]
        for block in copied_blocks:
            # astroplan's PriorityScheduler expects lower priorities to be more important, so calculate
            # 1000 - priority
            block.priority = 1000.0 - block.priority
            if block.priority < 0:
                block.priority = 0

            # it also doesn't match the requested observing windows exactly, so we make them a little smaller.
            for constraint in block.constraints:
                if isinstance(constraint, TimeConstraint):
                    constraint.min += 30 * u.second
                    constraint.max -= 30 * u.second

        # get start time for scheduler
        start = self._schedule_start
        now_plus_safety = Time.now() + self._safety_time * u.second
        if start is None or start < now_plus_safety:
            # if no ETA exists or is in the past, use safety time
            start = now_plus_safety

        # get running scheduled block, if any
        if self._current_task_id is None:
            log.info("No running block found.")
            running_task = None
        else:
            # get running task from archive
            log.info("Trying to find running block in current schedule...")
            now = Time.now()
            tasks = await self._task_archive.get_pending_tasks(
                now, now, include_running=True)
            if self._current_task_id in tasks:
                running_task = tasks[self._current_task_id]
            else:
                log.info("Running block not found in last schedule.")
                running_task = None

        # if start is before end time of currently running block, change that
        if running_task is not None:
            log.info("Found running block that ends at %s.", running_task.end)

            # get block end plus some safety
            block_end = running_task.end + 10.0 * u.second
            if start < block_end:
                start = block_end
                log.info(
                    "Start time would be within currently running block, shifting to %s.",
                    start.isot)

        # calculate end time
        end = start + TimeDelta(self._schedule_range * u.hour)

        # remove currently running block and filter by start time
        blocks = []
        for b in filter(
                lambda x: x.configuration["request"]["id"] != self.
                _current_task_id, copied_blocks):
            time_constraint_found = False
            # loop all constraints
            for c in b.constraints:
                if isinstance(c, TimeConstraint):
                    # we found a time constraint
                    time_constraint_found = True

                    # does the window start before the end of the scheduling range?
                    if c.min < end:
                        # yes, store block and break loop
                        blocks.append(b)
                        break
            else:
                # loop has finished without breaking
                # if no time constraint has been found, we still take the block
                if time_constraint_found is False:
                    blocks.append(b)

        # if need new update, skip here
        if self._need_update:
            log.info("Not running scheduler, since update was requested.")
            return

        # no blocks found?
        if len(blocks) == 0:
            log.info("No blocks left for scheduling.")
            await self._task_archive.update_schedule([], start)
            return

        # log it
        log.info(
            "Calculating schedule for %d schedulable block(s) starting at %s...",
            len(blocks), start)

        # we don't need any transitions
        transitioner = Transitioner()

        # create scheduler
        scheduler = PriorityScheduler(constraints,
                                      self.observer,
                                      transitioner=transitioner)

        # run scheduler
        time_range = Schedule(start, end)
        loop = asyncio.get_running_loop()
        schedule = await loop.run_in_executor(None, scheduler, blocks,
                                              time_range)

        # if need new update, skip here
        if self._need_update:
            log.info(
                "Not using scheduler results, since update was requested.")
            return

        # update
        await self._task_archive.update_schedule(schedule.scheduled_blocks,
                                                 start)
        if len(schedule.scheduled_blocks) > 0:
            log.info("Finished calculating schedule for %d block(s):",
                     len(schedule.scheduled_blocks))
            for i, block in enumerate(schedule.scheduled_blocks, 1):
                log.info(
                    "  #%d: %s to %s (%.1f)",
                    block.configuration["request"]["id"],
                    block.start_time.strftime("%H:%M:%S"),
                    block.end_time.strftime("%H:%M:%S"),
                    block.priority,
                )
        else:
            log.info("Finished calculating schedule for 0 blocks.")
예제 #10
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_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:

        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 (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)
    ]

    # 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
예제 #11
0
    def _schedule_thread(self):
        # only constraint is the night
        if self._twilight == 'astronomical':
            constraints = [AtNightConstraint.twilight_astronomical()]
        elif self._twilight == 'nautical':
            constraints = [AtNightConstraint.twilight_nautical()]
        else:
            raise ValueError('Unknown twilight type.')

        # we don't need any transitions
        transitioner = Transitioner()

        # run forever
        while not self.closing.is_set():
            # need update?
            if self._need_update:
                # reset need for update
                self._need_update = False

                # get start time for scheduler
                start = self._schedule_start
                now_plus_safety = Time.now() + self._safety_time * u.second
                if start is None or start < now_plus_safety:
                    # if no ETA exists or is in the past, use safety time
                    start = now_plus_safety
                end = start + TimeDelta(self._schedule_range * u.hour)

                # remove currently running block and filter by start time
                blocks = []
                for b in filter(
                        lambda b: b.configuration['request']['id'] != self.
                        _current_task_id, self._blocks):
                    time_constraint_found = False
                    # loop all constraints
                    for c in b.constraints:
                        if isinstance(c, TimeConstraint):
                            # we found a time constraint
                            time_constraint_found = True

                            # does the window start before the end of the scheduling range?
                            if c.min < end:
                                # yes, store block and break loop
                                blocks.append(b)
                                break
                    else:
                        # loop has finished without breaking
                        # if no time constraint has been found, we still take the block
                        if time_constraint_found is False:
                            blocks.append(b)

                # log it
                log.info(
                    'Calculating schedule for %d schedulable block(s) starting at %s...',
                    len(blocks), start)

                # init scheduler and schedule
                scheduler = SequentialScheduler(constraints,
                                                self.observer,
                                                transitioner=transitioner)
                time_range = Schedule(start, end)
                schedule = scheduler(blocks, time_range)

                # update
                self._task_archive.update_schedule(schedule.scheduled_blocks,
                                                   start)
                if len(schedule.scheduled_blocks) > 0:
                    log.info('Finished calculating schedule for %d block(s):',
                             len(schedule.scheduled_blocks))
                    for i, block in enumerate(schedule.scheduled_blocks, 1):
                        log.info('  #%d: %s to %s (%.1f)',
                                 block.configuration['request']['id'],
                                 block.start_time, block.end_time,
                                 block.priority)
                else:
                    log.info('Finished calculating schedule for 0 blocks.')

            # sleep a little
            self.closing.wait(1)