def define_constraints(minAltitude, earliestObs, latestObs): """ Define various constraints to define 'observability'. Format for times: YYYY-MM-DD Parameters ---------- minAltitude : int minimum local elevation in degree earliestObs : str Earliest time of observation latestObs : str Latest time of observation Returns ------- constraints : list list of constraints earliestObs : Time object Earliest time of observation latestObs : Time object Latest time of observation """ constraints = [AtNightConstraint.twilight_astronomical(), AltitudeConstraint(min=minAltitude*u.deg)] return constraints, Time(earliestObs), Time(latestObs)
def run_months(observers, nameList, args): targets = [FixedTarget(coord=lookuptarget(name),name=name) for name in nameList] targetLabelList, ylabelsize = makeTargetLabels(nameList,args) constraints = [ AltitudeConstraint(min=args.minAlt*u.deg), AtNightConstraint.twilight_astronomical(), ] outfn = args.outFileNameBase+"_monthly.pdf" with PdfPages(outfn) as pdf: for observer in observers: observability_months_table = months_observable(constraints,observer,targets,time_grid_resolution=1*u.hour) observability_months_grid = numpy.zeros((len(targets),12)) for i, observable in enumerate(observability_months_table): for jMonth in range(1,13): observability_months_grid[i,jMonth-1] = jMonth in observable observable_targets = targets observable_target_labels = targetLabelList ever_observability_months_grid = observability_months_grid if args.onlyEverObservable: target_is_observable = numpy.zeros(len(targets)) for iMonth in range(observability_months_grid.shape[1]): target_is_observable += observability_months_grid[:,iMonth] target_is_observable = target_is_observable > 0. # change to boolean numpy array observable_targets = [x for x, o in zip(targets,target_is_observable) if o] observable_target_labels = [x for x, o in zip(targetLabelList,target_is_observable) if o] ever_observability_months_grid = observability_months_grid[target_is_observable,:] fig, ax = mpl.subplots( figsize=(8.5,11), gridspec_kw={ "top":0.92, "bottom":0.03, "left":0.13, "right":0.98, }, tight_layout=False,constrained_layout=False ) extent = [-0.5, -0.5+12, -0.5, len(observable_targets)-0.5] ax.imshow(ever_observability_months_grid, extent=extent, origin="lower", aspect="auto", cmap=mpl.get_cmap("Greens")) ax.xaxis.tick_top() ax.invert_yaxis() ax.set_yticks(range(0,len(observable_targets))) ax.set_yticklabels(observable_target_labels, fontsize=ylabelsize) ax.set_xticks(range(12)) ax.set_xticklabels(["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]) ax.set_xticks(numpy.arange(extent[0], extent[1]), minor=True) ax.set_yticks(numpy.arange(extent[2], extent[3]), minor=True) ax.grid(which="minor",color="black",ls="-", linewidth=1) ax.tick_params(axis='y', which='minor', left=False, right=False) ax.tick_params(axis='x', which='minor', bottom=False, top=False) fig.suptitle(f"Monthly Observability at {observer.name}") fig.text(1.0,0.0,"Constraints: Astronomical Twilight, Altitude $\geq {:.0f}^\circ$".format(args.minAlt),ha="right",va="bottom") pdf.savefig(fig) print(f"Writing out file: {outfn}")
def compute_is_up(name, time): ''' Computes what V<11 objects in a catalog are up right now. args: name (str): one in: ['messier', 'ngc'] time (str): in 24hr format, e.g. "2017-04-17 21:00:00" returns: catalog with is_up calculated, sorted by v_mag. ''' import datetime from astropy.time import Time from astroplan import FixedTarget from astropy.coordinates import SkyCoord from astroplan import Observer, FixedTarget, AltitudeConstraint, \ AtNightConstraint, is_observable, is_always_observable, \ months_observable assert name in ['messier', 'ngc'] if name == 'messier': data_path = '../data/catalogs/messier.csv' elif name == 'ngc': data_path = '../data/catalogs/ngc.csv' df = pd.read_csv(data_path) # The search & target creation is slow for >~thousands of FixedTargets. NGC # catalog is 13226 objects. Take those with v_mag<11, since from Peyton we # likely won't go fainter. df = df.sort_values('v_mag') df = df[df['v_mag']<11] peyton = Observer(longitude=74.65139*u.deg, latitude=40.34661*u.deg, elevation=62*u.m, name='Peyton', timezone='US/Eastern') if name == 'ngc': ras = np.array(df['ra'])*u.hourangle decs = np.array(df['dec'])*u.degree names = np.array(df['ngc_id']) elif name == 'messier': ras = np.array(df['ra'])*u.hourangle decs = np.array(df['dec'])*u.degree names = np.array(df['messier_id']) targets = [FixedTarget(SkyCoord(ra=r, dec=d), name=n) for r, d, n in tuple(zip(ras, decs, names))] constraints = [AltitudeConstraint(10*u.deg, 82*u.deg), AtNightConstraint(max_solar_altitude=-3.*u.deg)] t_obs = Time(time) is_up = is_observable(constraints, peyton, targets, times=t_obs) df['is_up'] = is_up return df
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)
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 make_airmass_chart(name="WASP 4"): target = FixedTarget.from_name(name) observer = Observer.at_site('keck') constraints = [ AltitudeConstraint(min=20 * u.deg, max=85 * u.deg), AtNightConstraint.twilight_civil() ] best_months = months_observable(constraints, observer, [target]) # computed observability on "best_months" grid of 0.5 hr print('for {}, got best-months on 0.5 hour grid:'.format(name)) print(best_months) print('where 1 = Jan, 2 = Feb, etc.')
def __init__(self, lat, lon, elevation, ra, dec, discDate, airmassConstraint=2): self.airmassConstraint = airmassConstraint self.altConstraint = math.degrees(math.asin(1 / self.airmassConstraint)) self.location = EarthLocation(lat=lat, lon=lon, height=elevation * u.m) self.discDate = discDate self.observer = Observer(location=self.location, name="LT") self.target = SkyCoord(ra=ra, dec=dec, unit=(u.hourangle, u.deg)) self.constraints = [ AirmassConstraint(self.airmassConstraint), AtNightConstraint.twilight_astronomical() ] print("Discovery Date :", discDate.value)
def get_observability_fraction(name="WASP 4", site='keck', ra=None, dec=None, start_time=Time('2019-09-13 20:00:00'), end_time=Time('2020-07-31 20:00:00')): if isinstance(name,str) and ra is None and dec is None: target = FixedTarget.from_name(name) elif isinstance(ra,float) and isinstance(dec,float): target_coord = SkyCoord(ra=ra*u.deg, dec=dec*u.deg) target = FixedTarget(coord=target_coord, name=name) else: raise NotImplementedError('failed to make target') observer = Observer.at_site(site) constraints = [AltitudeConstraint(min=20*u.deg, max=85*u.deg), AirmassConstraint(3), AtNightConstraint.twilight_civil()] # over every day between start and end time, check if the observing # constraints are meetable. days = Time( np.arange(start_time.decimalyear, end_time.decimalyear, 1/(365.25)), format='decimalyear' ) frac, ever_observable = [], [] for day in days: table = observability_table(constraints, observer, [target], time_range=day) frac.append(float(table['fraction of time observable'])) ever_observable.append(bool(table['ever observable'])) ever_observable = np.array(ever_observable) frac = np.array(frac) return frac, ever_observable, days
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 vis(date, objects, obj_tab): #This tool is designed for the Magellan Telescope @ Las Camapanas Observatory, in Chile las = Observer.at_site('LCO') #both las and los are the locations of MagAO, but one is used for the plot and the other for the time lco = EarthLocation.of_site('Las Campanas Observatory') userEntered_list = list(objects.split(",")) target_list = userEntered_list targets = [] for i in range(1, len(obj_tab)): ra = (obj_tab.iloc[i, 2])[1:] + ' hours' dec = (obj_tab.iloc[i, 3])[1:] + ' degrees' print(ra + ',' + dec) targets.append( FixedTarget(coord=SkyCoord(ra=ra, dec=dec), name=target_list[i - 1])) constraints = [ AltitudeConstraint(10 * u.deg, 80 * u.deg), AirmassConstraint(5), AtNightConstraint.twilight_civil() ] start_time = las.sun_set_time(Time(date), which='nearest') end_time = las.sun_rise_time(Time(date), which='nearest') date = start_time.iso[:10] + ' to ' + end_time.iso[:10] time_range = Time([start_time, end_time]) # In[ ]: delta_t = end_time - start_time observe_time = start_time + delta_t * np.linspace(0, 1, 75) # In[ ]: # Are targets *ever* observable in the time range? ever_observable = is_observable(constraints, las, targets, time_range=time_range) # Are targets *always* observable in the time range? always_observable = is_always_observable(constraints, las, targets, time_range=time_range) # During what months are the targets ever observable? best_months = months_observable(constraints, las, targets) # In[ ]: table = observability_table(constraints, las, targets, time_range=time_range) print(table) table = table.to_pandas() np.savetxt( 'static/data/visibility.txt', table, fmt="%-30s", header= 'Target name ever observable always observable fraction of time observable' )
for i in range(0, l): targs.append( FixedTarget(coord=SkyCoord(ra=(t[i]['ra']), dec=(t[i]['dec']), unit=(u.hourangle, u.deg)), name=t[i]['alt_name'] + " [" + t[i]['iau_name'] + ", " + t[i]['ra'] + ", " + t[i]['dec'] + "]")) # Accounting for the fact that ra is in [hour min sec] and dec is in [deg arcmin arcsec] # the "name" attribute is all of the information just smashed together and I know it's unclean, please don't judge me # print((targs[len(targs)-1].name)) # This was just to check where errors occurred time_range = Time(["2018-04-15 00:01", "2018-04-30 23:59"]) # This just takes the whole swath of time, not refining it or anything cons = [AirmassConstraint(2), AtNightConstraint.twilight_astronomical()] # don't use civil twilight because astronomers aren't civil obs_tab = observability_table(cons, kitt, targs, time_range=time_range) # print(obs_tab) obs_targs = [] total_save = [] # total_save is to keep all the data, not just some of it for i in range(0, len(obs_tab)): if obs_tab[i]['ever observable']: obs_targs.append([ obs_tab[i]['target name'], obs_tab[i]['fraction of time observable'] ])
from astroplan import time_grid_from_range from astroplan import (AltitudeConstraint, AtNightConstraint, Constraint) from astroplan import (is_observable, is_always_observable, months_observable, observability_table) import pylab as pl pl.ion() observer = Observer.at_site('GBT', timezone='US/Eastern') time1 = astropy.time.Time('2021-02-01', location=observer.location) time2 = astropy.time.Time('2021-07-31', location=observer.location) time_range = astropy.time.Time([time1, time2]) GBTNight = AtNightConstraint() GBTNight.max_solar_altitude = 5 * u.deg class GBT3hoursAfterSunset(Constraint): """ Constraint requiring 3hrs setup for MUSTANG """ def compute_constraint(self, times, observer, targets): # we want the time since the sun went below 5 deg, # which is enough to start cooling the dish sunset = observer.sun_set_time(times, horizon=5 * u.deg) mask = times > sunset + astropy.time.TimeDelta(3 * u.hour)
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
# 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 _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)
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
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'] }
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
def plan_when_transits_will_occur( filename='targets.txt', observatory='Southern African Large Telescope', start='2017-06-22', end='2017-06-28', airmass_limit=2.5, moon_distance=10, do_secondary=True, method='by_night'): ''' Plan when targets will be visibile and transiting from a site. Inputs ------ filename : str A plain text file with the following columns: target : The name of the target (e.g. J0555-57). RA : The right ascension of the target (e.g. 05h55m32.62s). DEC : The declination of the target (e.g. -57d17m26.1s). epoch* : The epoch of the transit. Youc can either use: epoch_HJD-2400000 : HJD - 24500000 epoch_BJD-2455000 : MJD Period : The period of the system (days). Secondary : can be True or False depending on whether you want to see when the secondary transits will be. observatory : str The observatory you are observing from. See later for list of available observatories (accepted by astropy). start : str The first night of observation (e.g. 2017-08-31). end : str The last night of observation (e.g. 2017-09-10). airmass_limit : float The maximum airmass you want to observe through. moon_distance : float The closest the target can be t the moon in arcmins. do_secondary = True: Look for secondary eclipses assuming circularised orbits. Available observator names are: 'ALMA', 'Anglo-Australian Observatory', 'Apache Point', 'Apache Point Observatory', 'Atacama Large Millimeter Array', 'BAO', 'Beijing XingLong Observatory', 'Black Moshannon Observatory', 'CHARA', 'Canada-France-Hawaii Telescope', 'Catalina Observatory', 'Cerro Pachon', 'Cerro Paranal', 'Cerro Tololo', 'Cerro Tololo Interamerican Observatory', 'DCT', 'Discovery Channel Telescope', 'Dominion Astrophysical Observatory', 'Gemini South', 'Hale Telescope', 'Haleakala Observatories', 'Happy Jack', 'Jansky Very Large Array', 'Keck Observatory', 'Kitt Peak', 'Kitt Peak National Observatory', 'La Silla Observatory', 'Large Binocular Telescope', 'Las Campanas Observatory', 'Lick Observatory', 'Lowell Observatory', 'Manastash Ridge Observatory', 'McDonald Observatory', 'Medicina', 'Medicina Dish', 'Michigan-Dartmouth-MIT Observatory', 'Mount Graham International Observatory', 'Mt Graham', 'Mt. Ekar 182 cm. Telescope', 'Mt. Stromlo Observatory', 'Multiple Mirror Telescope', 'NOV', 'National Observatory of Venezuela', 'Noto', 'Observatorio Astronomico Nacional, San Pedro Martir', 'Observatorio Astronomico Nacional, Tonantzintla', 'Palomar', 'Paranal Observatory', 'Roque de los Muchachos', 'SAAO', 'SALT', 'SRT', 'Siding Spring Observatory', 'Southern African Large Telescope', 'Subaru', 'Subaru Telescope', 'Sutherland', 'Vainu Bappu Observatory', 'Very Large Array', 'W. M. Keck Observatory', 'Whipple', 'Whipple Observatory', 'aao', 'alma', 'apo', 'bmo', 'cfht', 'ctio', 'dao', 'dct', 'ekar', 'example_site', 'flwo', 'gemini_north', 'gemini_south', 'gemn', 'gems', 'greenwich', 'haleakala', 'irtf', 'keck', 'kpno', 'lapalma', 'lasilla', 'lbt', 'lco', 'lick', 'lowell', 'mcdonald', 'mdm', 'medicina', 'mmt', 'mro', 'mso', 'mtbigelow', 'mwo', 'noto', 'ohp', 'paranal', 'salt', 'sirene', 'spm', 'srt', 'sso', 'tona', 'vbo', 'vla'. ''' ################### # Try reading table ################### try: target_table = Table.read(filename, format='ascii') except: raise ValueError( 'I cant open the target file (make sure its ascii with the following first line:\ntarget RA DEC epoch_HJD-2400000 Period Secondary' ) ############################## # try reading observation site ############################## try: observation_site = coord.EarthLocation.of_site(observatory) observation_handle = Observer(location=observation_site) observation_handle1 = Observer.at_site(observatory) except: print(coord.EarthLocation.get_site_names()) raise ValueError('The site is not understood') ################################### # Try reading start and end times ################################### try: start_time = Time(start + ' 12:01:00', location=observation_site) end_time = Time(end + ' 12:01:00', location=observation_site) number_of_nights = int(end_time.jd - start_time.jd) time_range = Time([start + ' 12:01:00', end + ' 12:01:00']) print('Number of nights: {}'.format(number_of_nights)) except: raise ValueError('Start and end times not understood') ##################### # Now do constraints ##################### #try: constraints = [ AltitudeConstraint(0 * u.deg, 90 * u.deg), AirmassConstraint(3), AtNightConstraint.twilight_civil() ] #except: # raise ValueError('Unable to get set constraints') if method == 'by_night': for i in range(number_of_nights): start_time_tmp = start_time + TimeDelta( i, format='jd') # get start time (doesent need to be accurate) end_time_tmp = start_time + TimeDelta( i + 1, format='jd') # get start time (doesent need to be accurate) print('#' * 80) start_time_tmpss = start_time_tmp.datetime.ctime().split( ) # ['Fri', 'Dec', '24', '12:00:00', '2010'] print('Night {} - {} {} {} {}'.format(i + 1, start_time_tmpss[0], start_time_tmpss[2], start_time_tmpss[1], start_time_tmpss[-1])) print('#' * 80) # Now print Almnac information (sunset and end of evening twilight print('Almnac:') sun_set = observation_handle.sun_set_time(start_time_tmp, which='next') print('Sunset:\t\t\t\t\t\t\t' + sun_set.utc.datetime.ctime()) twilight_evening_astronomical = observation_handle.twilight_evening_astronomical( start_time_tmp, which='next') # -18 twilight_evening_nautical = observation_handle.twilight_evening_nautical( start_time_tmp, which='next') # -12 twilight_evening_civil = observation_handle.twilight_evening_civil( start_time_tmp, which='next') # -6 deg print('Civil evening twilight (-6 deg) (U.T.C):\t\t' + twilight_evening_civil.utc.datetime.ctime()) print('Nautical evening twilight (-12 deg) (U.T.C):\t\t' + twilight_evening_nautical.utc.datetime.ctime()) print('Astronomical evening twilight (-18 deg) (U.T.C):\t' + twilight_evening_astronomical.utc.datetime.ctime()) print('\n') twilight_morning_astronomical = observation_handle.twilight_morning_astronomical( start_time_tmp, which='next') # -18 twilight_morning_nautical = observation_handle.twilight_morning_nautical( start_time_tmp, which='next') # -12 twilight_morning_civil = observation_handle.twilight_morning_civil( start_time_tmp, which='next') # -6 deg print('Astronomical morning twilight (-18 deg) (U.T.C):\t' + twilight_morning_astronomical.utc.datetime.ctime()) print('Nautical morning twilight (-12 deg) (U.T.C):\t\t' + twilight_morning_nautical.utc.datetime.ctime()) print('Civil morning twilight (-6 deg) (U.T.C):\t\t' + twilight_morning_civil.utc.datetime.ctime()) sun_rise = observation_handle.sun_rise_time(start_time_tmp, which='next') print('Sunrise:\t\t\t\t\t\t' + sun_rise.utc.datetime.ctime()) print('\n') # stuff for creating plot plot_mids = [] plot_names = [] plot_widths = [] for j in range(len(target_table)): # Extract information star_coordinates = coord.SkyCoord('{} {}'.format( target_table['RA'][j], target_table['DEC'][j]), unit=(u.hourangle, u.deg), frame='icrs') star_fixed_coord = FixedTarget(coord=star_coordinates, name=target_table['target'][j]) #################### # Get finder image #################### ''' plt.close() try: finder_image = plot_finder_image(star_fixed_coord,reticle=True,fov_radius=10*u.arcmin) except: pass plt.savefig(target_table['target'][j]+'_finder_chart.eps') ''' P = target_table['Period'][j] Secondary_transit = target_table['Secondary'][j] transit_half_width = TimeDelta( target_table['width'][j] * 60 * 60 / 2, format='sec') # in seconds for a TimeDelta # now convert T0 to HJD -> JD -> BJD so we can cout period if 'epoch_HJD-2400000' in target_table.colnames: #print('Using HJD-2400000') T0 = target_table['epoch_HJD-2400000'][j] T0 = Time(T0 + 2400000, format='jd') # HJD given by WASP ltt_helio = T0.light_travel_time(star_coordinates, 'heliocentric', location=observation_site) T0 = T0 - ltt_helio # HJD -> JD ltt_bary = T0.light_travel_time(star_coordinates, 'barycentric', location=observation_site) T0 = T0 + ltt_bary # JD -> BJD elif 'epoch_BJD-2455000' in target_table.colnames: #print('Using BJD-2455000') T0 = target_table['epoch_BJD-2455000'][j] + 2455000 T0 = Time(T0, format='jd') # BJD else: print('\n\n\n\n FAILE\n\n\n\n') continue ########################################################## # Now start from T0 and count in periods to find transits ########################################################## # convert star and end time to BJD ltt_bary_start_time = start_time_tmp.light_travel_time( star_coordinates, 'barycentric', location=observation_site) # + TimeDelta(i,format='jd') start_time_bary = start_time_tmp + ltt_bary_start_time # + TimeDelta(i,format='jd') # convert start time to BJD ltt_bary_end_time_tmp = end_time_tmp.light_travel_time( star_coordinates, 'barycentric', location=observation_site) # + TimeDelta(i,format='jd') end_time_bary = end_time_tmp + ltt_bary_start_time #+ TimeDelta(i+1,format='jd') # convert end time to BJD and add 1 day 12pm -> 12pm the next day elapsed = end_time_bary - start_time_bary # now this is 24 hours from the start day 12:00 pm # now count transits time = Time(T0.jd, format='jd') # make a temporary copy transits = [] primary_count, secondary_count = 0, 0 while time.jd < end_time_bary.jd: if (time.jd > start_time_bary.jd) and (time.jd < end_time_bary.jd): if is_observable(constraints, observation_handle, [star_fixed_coord], times=[time])[0] == True: transits.append(time) primary_count += 1 if Secondary_transit == 'yes': timesecondary = time + TimeDelta(P / 2, format='jd') if (timesecondary.jd > start_time_bary.jd) and ( timesecondary.jd < end_time_bary.jd): if is_observable(constraints, observation_handle, [star_fixed_coord], times=[timesecondary])[0] == True: transits.append(timesecondary) secondary_count += 1 time = time + TimeDelta(P, format='jd') # add another P to T0 # Now find visible transits transits = [ i for i in transits if is_observable(constraints, observation_handle, [star_fixed_coord], times=[i])[0] == True ] if len(transits) == 0: message = '{} has no transits.'.format( target_table['target'][j]) print('-' * len(message)) print(message) print('-' * len(message)) print('\n') plt.close() continue else: message = '{} has {} primary transits and {} secondary transits.'.format( target_table['target'][j], primary_count, secondary_count) print('-' * len(message)) print(message) print('RA: {}'.format(target_table['RA'][j])) print('DEC: {}'.format(target_table['DEC'][j])) print('Epoch: 2000') print('T0 (BJD): {}'.format(T0.jd)) print('Period: {}'.format(P)) print('Transit width (hr): {}'.format( target_table['width'][j])) print('-' * len(message)) print('\n') for i in transits: # currently transit times are in BJD (need to convert to HJD to check ltt_helio = i.light_travel_time(star_coordinates, 'barycentric', location=observation_site) ii = i - ltt_helio ltt_helio = ii.light_travel_time(star_coordinates, 'heliocentric', location=observation_site) ii = ii + ltt_helio transit_1 = i - transit_half_width - TimeDelta( 7200, format='sec') # ingress - 2 hr transit_2 = i - transit_half_width - TimeDelta( 3600, format='sec') # ingress - 2 hr transit_3 = i - transit_half_width # ingress transit_4 = i + transit_half_width # egress transit_5 = i + transit_half_width + TimeDelta( 3600, format='sec') # ingress - 2 hr transit_6 = i + transit_half_width + TimeDelta( 7200, format='sec') # ingress - 2 hr if (((i.jd - time.jd) / P) - np.floor( (i.jd - time.jd) / P) < 0.1) or (( (i.jd - time.jd) / P) - np.floor( (i.jd - time.jd) / P) > 0.9): print('Primary Transit:') print('-' * len('Primary Transit')) if 0.4 < ((i.jd - time.jd) / P) - np.floor( (i.jd - time.jd) / P) < 0.6: print('Secondary Transit') print('-' * len('Secondary Transit')) ################## # now get sirmass ################## altaz = star_coordinates.transform_to( AltAz(obstime=transit_1, location=observation_site)) hourangle = observation_handle1.target_hour_angle( transit_1, star_coordinates) hourangle = 24 * hourangle.degree / 360 if hourangle > 12: hourangle -= 24 print('Ingress - 2hr (U.T.C):\t\t\t\t\t' + transit_1.utc.datetime.ctime() + '\tAirmass: {:.2f}\tHA:{:.2f}'.format( altaz.secz, hourangle)) altaz = star_coordinates.transform_to( AltAz(obstime=transit_2, location=observation_site)) hourangle = observation_handle1.target_hour_angle( transit_2, star_coordinates) hourangle = 24 * hourangle.degree / 360 if hourangle > 12: hourangle -= 24 print('Ingress - 1hr (U.T.C):\t\t\t\t\t' + transit_2.utc.datetime.ctime() + '\tAirmass: {:.2f}\tHA:{:.2f}'.format( altaz.secz, hourangle)) altaz = star_coordinates.transform_to( AltAz(obstime=transit_3, location=observation_site)) hourangle = observation_handle1.target_hour_angle( transit_3, star_coordinates) hourangle = 24 * hourangle.degree / 360 if hourangle > 12: hourangle -= 24 print('Ingress (U.T.C):\t\t\t\t\t' + transit_3.utc.datetime.ctime() + '\tAirmass: {:.2f}\tHA:{:.2f}'.format( altaz.secz, hourangle)) altaz = star_coordinates.transform_to( AltAz(obstime=i, location=observation_site)) hourangle = observation_handle1.target_hour_angle( i, star_coordinates) hourangle = 24 * hourangle.degree / 360 if hourangle > 12: hourangle -= 24 print('Mid transit (U.T.C):\t\t\t\t\t' + i.utc.datetime.ctime() + '\tAirmass: {:.2f}\tHA:{:.2f}'.format( altaz.secz, hourangle)) altaz = star_coordinates.transform_to( AltAz(obstime=transit_4, location=observation_site)) hourangle = observation_handle1.target_hour_angle( transit_4, star_coordinates) hourangle = 24 * hourangle.degree / 360 if hourangle > 12: hourangle -= 24 print('Egress (U.T.C):\t\t\t\t\t\t' + transit_4.utc.datetime.ctime() + '\tAirmass: {:.2f}\tHA:{:.2f}'.format( altaz.secz, hourangle)) altaz = star_coordinates.transform_to( AltAz(obstime=transit_5, location=observation_site)) hourangle = observation_handle1.target_hour_angle( transit_5, star_coordinates) hourangle = 24 * hourangle.degree / 360 if hourangle > 12: hourangle -= 24 print('Egress + 1hr (U.T.C):\t\t\t\t\t' + transit_5.utc.datetime.ctime() + '\tAirmass: {:.2f}\tHA:{:.2f}'.format( altaz.secz, hourangle)) altaz = star_coordinates.transform_to( AltAz(obstime=transit_6, location=observation_site)) hourangle = observation_handle1.target_hour_angle( transit_6, star_coordinates) hourangle = 24 * hourangle.degree / 360 if hourangle > 12: hourangle -= 24 print('Egress + 2hr (U.T.C):\t\t\t\t\t' + transit_6.utc.datetime.ctime() + '\tAirmass: {:.2f}\tHA:{:.2f}'.format( altaz.secz, hourangle)) print('HJD {} (to check with http://var2.astro.cz/)\n'. format(ii.jd)) # append stuff for plots plot_mids.append(i) # astropy Time plot_names.append(target_table['target'][j]) plot_widths.append(target_table['width'][j]) # Now plot plt.close() if len(plot_mids) == 0: continue date_formatter = dates.DateFormatter('%H:%M') #ax.xaxis.set_major_formatter(date_formatter) # now load dummy transit lightcurves xp, yp = np.load('lc.npy') xs, ys = np.load('lcs.npy') # x = np.linspace(0, 2*np.pi, 400) # y = np.sin(x**2) subplots_adjust(hspace=0.000) number_of_subplots = len( plot_names) # number of targets transiting that night time = sun_set + np.linspace(-1, 14, 100) * u.hour # take us to sunset for i, v in enumerate(xrange(number_of_subplots)): # exctract params width = plot_widths[v] name = plot_names[v] mid = plot_mids[v] # now set up dummy lc plot x_tmp = mid + xp * (width / 2) * u.hour # get right width in hours # now set up axis v = v + 1 ax1 = subplot(number_of_subplots, 1, v) ax1.xaxis.set_major_formatter(date_formatter) if v == 1: ax1.set_title(start) # plot transit model ax1.plot_date(x_tmp.plot_date, ys, 'k-') # plot continuum #xx =time.plot_date #xx = [uu for uu in xx if (uu<min(x_tmp.plot_date)) or (uu>max(x_tmp.plot_date))] #ax1.plot_date(xx, np.ones(len(xx)),'k--', alpha=0.3) ax1.set_xlim(min(time.plot_date), max(time.plot_date)) #ax1.plot_date(mid.plot_date, 0.5, 'ro') plt.setp(ax1.get_xticklabels(), rotation=30, ha='right') ax1.set_ylabel(name, rotation=45, labelpad=20) twilights = [ (sun_set.datetime, 0.0), (twilight_evening_civil.datetime, 0.1), (twilight_evening_nautical.datetime, 0.2), (twilight_evening_astronomical.datetime, 0.3), (twilight_morning_astronomical.datetime, 0.4), (twilight_morning_nautical.datetime, 0.3), (twilight_morning_civil.datetime, 0.2), (sun_rise.datetime, 0.1), ] for ii, twii in enumerate(twilights[1:], 1): ax1.axvspan(twilights[ii - 1][0], twilights[ii][0], ymin=0, ymax=1, color='grey', alpha=twii[1]) ax1.grid(alpha=0.5) ax1.get_yaxis().set_ticks([]) if v != number_of_subplots: ax1.get_xaxis().set_ticks([]) plt.xlabel('Time [U.T.C]') #plt.tight_layout() #plt.savefig('test.eps',format='eps') plt.show()
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.")
from django.test import TestCase from mop.toolbox.obs_details import all_night_moon_sep, calculate_visibility from mop.toolbox.LCO_obs_locs import choose_loc OGG = choose_loc('OGG') v_test_target = ['Sirius', 100.7362500 * u.deg, -16.6459444 * u.deg] v_date = Time("2019-12-25 00:00:00", scale='utc') v_coords = SkyCoord(v_test_target[1], v_test_target[2], frame='icrs') v_obs_begin = OGG.twilight_evening_astronomical(v_date, which='nearest') v_obs_end = OGG.twilight_morning_astronomical(v_date, which='next') v_observing_range = [v_obs_begin, v_obs_end] constraints = [ AirmassConstraint(2.0), AltitudeConstraint(20 * u.deg, 85 * u.deg), AtNightConstraint.twilight_astronomical() ] ever_observable = is_observable(constraints, OGG, v_coords, time_range=v_observing_range) v_fail_start = Time("2019-12-24 10:00:00", scale='utc') v_fail_end = Time("2019-12-25 10:00:00", scale='utc') class TestVisibilityCalc(TestCase): def test_timeobj(self): self.assertEqual(v_date.scale, 'utc') self.assertEqual(v_date.value, '2019-12-25 00:00:00.000')
async def get_schedulable_blocks(self) -> List[ObservingBlock]: """Returns list of schedulable blocks. Returns: List of schedulable blocks """ # check if self._portal_instrument_type is None: raise ValueError("No instrument type for portal set.") # get data schedulable = await self._portal_get( urljoin(self._url, "/api/requestgroups/schedulable_requests/")) # get proposal priorities data = await self._portal_get(urljoin(self._url, "/api/proposals/")) tac_priorities = {p["id"]: p["tac_priority"] for p in data["results"]} # loop all request groups blocks = [] for group in schedulable: # get base priority, which is tac_priority * ipp_value proposal = group["proposal"] if proposal not in tac_priorities: log.error('Could not find proposal "%s".', proposal) continue base_priority = group["ipp_value"] * tac_priorities[proposal] # loop all requests in group for req in group["requests"]: # still pending? if req["state"] != "PENDING": continue # duration duration = req["duration"] * u.second # time constraints time_constraints = [ TimeConstraint(Time(wnd["start"]), Time(wnd["end"])) for wnd in req["windows"] ] # loop configs for cfg in req["configurations"]: # get instrument and check, whether we schedule it instrument = cfg["instrument_type"] if instrument.lower( ) != self._portal_instrument_type.lower(): continue # target t = cfg["target"] target = SkyCoord(t["ra"] * u.deg, t["dec"] * u.deg, frame=t["type"].lower()) # constraints c = cfg["constraints"] constraints = [] if "max_airmass" in c and c["max_airmass"] is not None: constraints.append( AirmassConstraint(max=c["max_airmass"], boolean_constraint=False)) if "min_lunar_distance" in c and c[ "min_lunar_distance"] is not None: constraints.append( MoonSeparationConstraint( min=c["min_lunar_distance"] * u.deg)) if "max_lunar_phase" in c and c[ "max_lunar_phase"] is not None: constraints.append( MoonIlluminationConstraint( max=c["max_lunar_phase"])) # if max lunar phase <= 0.4 (which would be DARK), we also enforce the sun to be <-18 degrees if c["max_lunar_phase"] <= 0.4: constraints.append( AtNightConstraint.twilight_astronomical()) # priority is base_priority times duration in minutes # priority = base_priority * duration.value / 60. priority = base_priority # create block block = ObservingBlock( FixedTarget(target, name=req["id"]), duration, priority, constraints=[*constraints, *time_constraints], configuration={"request": req}, ) blocks.append(block) # return blocks return blocks
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 )
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
def observability(self, json_exoplanet_array, longitude, latitude, elevation, start_date, end_date, min_altitude, max_altitude, resolution): '''background task to (slowly) find planet observabilities''' # create Observer instance if elevation is None: elevation = 0 custom_location = Observer(longitude=longitude*u.deg, latitude=latitude*u.deg, elevation=elevation*u.m) # time range if start_date is not None: start_date = Time(datetime.combine(datetime.fromisoformat(start_date), datetime.min.time()), out_subfmt='date') else: start_date = Time(datetime.combine(datetime.now(), datetime.min.time()), out_subfmt='date') if end_date is not None: end_date = Time(datetime.combine(datetime.fromisoformat(end_date), datetime.min.time()), out_subfmt='date') else: end_date = start_date + 30*u.day time_range = Time([start_date, end_date]) # altitude and night time constraints if min_altitude is None: min_altitude = 0 if max_altitude is None: max_altitude = 90 constraints = [AltitudeConstraint(min_altitude*u.deg, max_altitude*u.deg), AtNightConstraint.twilight_astronomical()] # calculate if observable within lookahead time from now if resolution is None: resolution = 0.5 # since we had to use a json string as argument, recreate the custom_exoplanet_array from the json custom_exoplanet_array = [] json_unload = json.loads(json_exoplanet_array) # unpack json to make a dictionary for dict_ in json_unload: planet = Exoplanet() # copy properties back over from dictionary for prop, val in dict_.items(): functions.rsetattr(planet, prop, val) custom_exoplanet_array.append(planet) # trying to split array into chunks so we can say the task has been completed to the nearest percent num_chunks = round(len(custom_exoplanet_array)/800 * (resolution/0.5)**-1 * ((end_date-start_date).jd/30)**2 * 10) if num_chunks == 0: num_chunks = 1 exoplanet_array_chunks = np.array_split(custom_exoplanet_array, num_chunks) # split exoplanet array into num_chunk parts planets_done = 0 # planets calculated for so far planets_lost = 0 # planets removed so far recombined_chunks = [] for chunk in exoplanet_array_chunks: # make an array of FixedTarget instances target_table = [] for planet in chunk: target_table.append((planet.name, planet.host.ra, planet.host.dec)) targets = [FixedTarget(coord=SkyCoord(ra=ra*u.deg, dec=dec*u.deg), name=name) for name, ra, dec in target_table] # calculate ever_observable = is_observable(constraints, custom_location, targets, time_range=time_range, time_grid_resolution=resolution*u.hour) # note unobservables to_pop = [] for i in range(len(chunk)): chunk[i].observable = bool(ever_observable[i]) if chunk[i].observable == False: to_pop.append(i) # remove unobservables chunk = np.delete(chunk, to_pop) # record numbers planets_done += len(targets) planets_lost += len(targets) - len(chunk) # write json recombined_chunks += chunk.tolist() observable_exoplanet_array = [planet for planet in recombined_chunks if planet.observable] json_exoplanet_array = functions.planet_array_to_json_array(observable_exoplanet_array, 'host_') # graph of decision metric vs rank tooltip_dict = { 'name' : [], 'mass' : [], 'radius' : [], 'orbital_period' : [], 'semi_major_axis' : [], 'temp_calculated' : [], 'detection_type' : [], 'decision_metric' : [], 'filter' : [], 't_exp' : [], } graph = functions.metric_rank_bar_graph(observable_exoplanet_array, tooltip_dict) # update status self.update_state(state='PROGRESS', meta={'current' : planets_done, 'removed' : planets_lost, 'total' : len(custom_exoplanet_array), 'exoplanet_array' : json_exoplanet_array, 'graph' : graph, 'finished' : 'n'}) return {'current': planets_done, 'removed': planets_lost, 'total': len(custom_exoplanet_array), 'exoplanet_array': json_exoplanet_array, 'graph': graph, 'finished': 'y'}
def find_observability(exoplanet_array, default_lookahead, longitude, latitude, elevation, start_date, end_date, min_altitude, max_altitude, resolution, flash, display): '''Function to calculate''' '''# find timezone timezone_name = tf.timezone_at(lng=longitude, lat=latitude) delta_degree = 1 while timezone_name is None: delta_degree += 1 timezone_name = tf.closest_timezone_at(lng=longitude, lat=latitude, delta_degree=delta_degree) if flash: flash.append('Timezone : ' + timezone_name) print(timezone_name)''' # create Observer instance if elevation is None: elevation = 0 custom_location = Observer(longitude=longitude * u.deg, latitude=latitude * u.deg, elevation=elevation * u.m) # make an array of FixedTarget instances target_table = [] for planet in exoplanet_array: target_table.append((planet.name, planet.host.ra, planet.host.dec)) targets = [ FixedTarget(coord=SkyCoord(ra=ra * u.deg, dec=dec * u.deg), name=name) for name, ra, dec in target_table ] # time range if start_date is not None: start_date = Time(datetime.combine(start_date, datetime.min.time()), out_subfmt='date') else: start_date = Time(datetime.combine(datetime.now(), datetime.min.time()), out_subfmt='date') if end_date is not None: end_date = Time(datetime.combine(end_date, datetime.min.time()), out_subfmt='date') else: end_date = start_date + default_lookahead * u.day time_range = Time([start_date, end_date]) #time_range_int = # no. days as an int if display: flash.append('Start date : ' + str(start_date.iso)) flash.append('End date : ' + str(end_date.iso)) # altitude and night time constraints if min_altitude is None: min_altitude = 0 if max_altitude is None: max_altitude = 90 constraints = [ AltitudeConstraint(min_altitude * u.deg, max_altitude * u.deg), AtNightConstraint.twilight_astronomical() ] # calculate if observable within lookahead time from now if resolution is None: resolution = 0.5 # new route, faster by not bothering to recheck planets already found to be observable # UPDATE, ASTROPY IS ALREADY OPTIMISED FOR THIS, THIS TAKES 10 TIMES LONGER, LEAVING IN FOR DEMONSTRATION PURPOSES ONLY (commented out lines in 'old route' are also only for demonstrating the difference) # start = time.perf_counter() # ever_observable = [False] * len(exoplanet_array) # day = 0 # while start_date + day*u.day != end_date: # print(str(start_date + day*u.day) + ', ' + str(end_date)) # for i in range(len(ever_observable)): # if ever_observable[i] == False: # ever_observable[i] = is_observable(constraints, custom_location, [targets[i]], time_range=Time([start_date+day*u.day, start_date+(day+1)*u.day]), time_grid_resolution=resolution*u.hour)[0] # print(str(day) + ', ' + str(i) + ', ' + targets[i].name + ', ' + str(ever_observable[i])) # day += 1 # end = time.perf_counter() # print(str(end - start) + ' seconds fast') # new route, faster by not bothering to recheck planets already found to be observable # UPDATE, ASTROPY IS ALREADY OPTIMISED FOR THIS, THIS TAKES 10 TIMES LONGER, LEAVING IN FOR DEMONSTRATION PURPOSES ONLY (commented out lines in 'old route' are also only for demonstrating the difference) # start = time.perf_counter() # ever_observable = [False] * len(exoplanet_array) # day = 0 # for i in range(len(ever_observable)): # for day in range(30): # print(str(start_date + day*u.day) + ', ' + str(end_date)) # if ever_observable[i] == False: # ever_observable[i] = is_observable(constraints, custom_location, [targets[i]], time_range=Time([start_date+day*u.day, start_date+(day+1)*u.day]), time_grid_resolution=resolution*u.hour)[0] # print(str(day) + ', ' + str(i) + ', ' + targets[i].name + ', ' + str(ever_observable[i])) # else: break # end = time.perf_counter() # print(str(end - start) + ' seconds fast') # trying to split array into chunks so we can say the task has been completed to the nearest percent # tosplit_target_array = copy.deepcopy(targets) # start = time.perf_counter() # target_chunks = np.array_split(tosplit_target_array, 10) # split targets into 10 parts (not 100, this makes the calculation time too long) # for i in range(len(target_chunks)): # is_observable(constraints, custom_location, target_chunks[i].tolist(), time_range=time_range, time_grid_resolution=resolution*u.hour) # #print('%i calculated' %i) # end = time.perf_counter() # print(str(end - start) + ' seconds fast') # old route, slow, commented out lines are for demonstrating that it is better than a manual method by a factor of 10 start = time.perf_counter() ever_observable = is_observable(constraints, custom_location, targets, time_range=time_range, time_grid_resolution=resolution * u.hour) end = time.perf_counter() print(str(end - start) + ' seconds slow') for i in range(len(exoplanet_array)): exoplanet_array[i].observable = ever_observable[i] if display: flash.append('Timing resolution = {} hrs'.format(resolution))
def construct_plan(data, site, start_time, end_time, constraints=None, max_priority=3): if constraints is None: constraints = [ AltitudeConstraint(10 * u.deg, 80 * u.deg), AirmassConstraint(5), AtNightConstraint.twilight_civil() ] data = data.sort_values(by=["Add. Data Priority", "RA"], ascending=[1, 1]) time_range = Time([start_time, end_time]) # targets.lis format: # NAME # <RA hh:mm:ss.ss> <DEC dd:mm:ss.s> # <HMJD/BMJD T0 err P err # # NAME... # ENTRY = "{}\n{} {}\n{} linear {} {} {} {}\n\n" targets_list = "" targets_prg = "" targets_notes = "" for name, row in data.iterrows(): row['RA'], row['Dec'] = row['RA'].replace(" ", ":"), row['Dec'].replace( " ", ":") target = FixedTarget(coord=SkyCoord(ra=Angle(row['RA'], unit='hourangle'), dec=Angle(row['Dec'], unit='degree')), name=name) # Does the target rise above the horizon? ever_observable = is_observable(constraints, site, target, time_range=time_range) # Parse the ephemeris data try: T0, T0_err = row['T(0) +/- (d)'].replace(" ", "").split("(") calendar, T0 = T0.split("=") T0_err = T0_err.replace(")", "") N = len(T0.split(".")[1]) - len(T0_err) T0_err = "0.{}{}".format(("0" * N), T0_err) P, P_err = row['P +/- (d)'].split("(") P_err = P_err.replace(")", "") M = len(P.split(".")[1]) - len(P_err) P_err = "0.{}{}".format(("0" * M), P_err) except ValueError: print("Failed to extract row! {}".format(name)) continue # Get the priority of this system try: priority = int(row['Add. Data Priority']) except ValueError: continue # Logic about if we want to use this target writeme = ever_observable[0] and (priority <= max_priority) # If true, add to the list if writeme: # Output data formats line = "{}\n{} {}\n{} linear {} {} {} {}\n\n".format( name, row['RA'], row['Dec'], calendar, T0, T0_err, P, P_err) prgline = "{}\n0.7 1.3 3 8\n\n".format(name) notesline = '{}: "{}"\n\n\n'.format(name, row['Target Notes']) targets_list += line targets_prg += prgline targets_notes += notesline # Write out files with open(os.path.join('OUTPUT', 'targets.lis'), 'w') as f: f.write(targets_list) with open(os.path.join('OUTPUT', 'targets.prg'), 'w') as f: f.write(targets_prg) with open(os.path.join('OUTPUT', 'targets.txt'), 'w') as f: f.write(targets_notes)
def main(event, context): filename = 'MS190311l-1-Preliminary' # Get targetlist target_list = 'triggers/%s_bayestar.csv'%filename # replace with your object key s3 = boto3.resource('s3') print(target_list) s3.Bucket(bucketname).download_file(target_list, '/tmp/%s_targetlist.csv'%filename) galaxies = Table.read('/tmp/%s_targetlist.csv'%filename) del galaxies['col0'] galaxies = np.array(galaxies.as_array().tolist()) """Get the full galaxy list, and find which are good to observe at NOT""" # Setup observer time = Time.now() NOT = Observer.at_site("lapalma") tel_constraints = [AtNightConstraint.twilight_civil(), AirmassConstraint(max = 5)] # Check if nighttime if not NOT.is_night(time): sunset_tonight = NOT.sun_set_time(time, which='nearest') dt_sunset = (sunset_tonight - time) print("Daytime at the NOT! Preparing a plan for observations starting next sunset in ~ %s hours."%(int(dt_sunset.sec/3600))) time = sunset_tonight + 5*u.minute else: print("It's nighttime at the NOT! Preparing a plan immeidately.") # Get target list galaxycoord=SkyCoord(ra=galaxies[:, 1]*u.deg,dec=galaxies[:, 2]*u.deg) targets = [FixedTarget(coord=SkyCoord(ra=ra*u.deg, dec=dec*u.deg), name=int(name)) for name, ra, dec in galaxies[:, :3]] # Construct astroplan OBs blocks = [] exposure = 300*u.second read_out = 20 * u.second for priority, targ in enumerate(targets): for bandpass in ['r']: b = ObservingBlock.from_exposures( targ, priority, exposure, 1, read_out, configuration={'filter': bandpass}) blocks.append(b) # Transitioner between targets slew_rate = 10.8*u.deg/u.second transitioner = Transitioner( slew_rate, { 'filter': { ('g', 'r'): 30 * u.second, ('i', 'z'): 30 * u.second, 'default': 30 * u.second } }) # Initialize the priority scheduler with the constraints and transitioner prior_scheduler = SequentialScheduler(constraints = tel_constraints, observer = NOT, transitioner = transitioner) # Initialize a Schedule object, to contain the new schedule around night night_length = NOT.sun_set_time(time, which='nearest') - NOT.sun_rise_time(time, which='nearest') noon_before = time - 4 * u.hour noon_after = time + 16 * u.hour priority_schedule = Schedule(noon_before, noon_after) # Call the schedule with the observing blocks and schedule to schedule the blocks prior_scheduler(blocks, priority_schedule) # Remove transition blocks to read observing order priority_schedule_table = priority_schedule.to_table() mask = priority_schedule_table["target"] != "TransitionBlock" pruned_schedule = priority_schedule_table[mask] idxs = np.arange(0, len(pruned_schedule["target"])) # pl.figure(figsize = (14,6)) # plot_schedule_airmass(priority_schedule, show_night=True) # pl.legend(loc = "upper right") # schedule_path = filename+"schedule.pdf" # pl.savefig(schedule_path) # pl.clf() # print("Finished preparing an observing plan.") # s3.Bucket(bucketname).upload_file(schedule_path, 'triggers/%s'%schedule_path) instrumements = ["ALFOSC"] nothing_to_observe = True for tel in range(0, len(instrumements)): print("Writing a plan for {}".format(instrumements[tel])) outlist = [0]*galaxies.shape[0] for i in range(tel, galaxies.shape[0], len(instrumements)): ra = Angle(galaxies[i, 1] * u.deg) dec = Angle(galaxies[i, 2] * u.deg) mask = pruned_schedule['target'].astype("int") == int(galaxies[i, 0]) targ_row = pruned_schedule[mask] idx = idxs[mask] # Get observing scheduling rank and airmass at observing time. try: airm = NOT.altaz(Time(targ_row["start time (UTC)"].data), targets[i]).secz t_s = targ_row["start time (UTC)"].data t_e = targ_row["end time (UTC)"].data except: print("GLADE target name %s not found in schedule. Probably not visible. Replacing entry with -99"%(galaxies[i, 0])) idx = -99 airm = -99 outlist[i] = int(galaxies[i, 0]), ra.to_string( unit=u.hourangle, sep=':', precision=2, pad=True), dec.to_string( sep=':', precision=2, alwayssign=True, pad=True), idx, airm, galaxies[i, 3], galaxies[i, 4], galaxies[ i, 5], t_s, t_e header = ["GladeID", "RA", "Dec", "Observing number", "Airmass at observing time", "Distance", "B-band luminosity", "Probability", "Schduled integration start", "Schduled integration end"] outframe = Table(np.array(outlist), names=header) csv_path = filename+"_schedule.csv" ascii.write(outframe, "/tmp/"+csv_path, format='csv', overwrite=True, fast_writer=False) s3.Bucket(bucketname).upload_file("/tmp/"+csv_path, 'triggers/%s'%csv_path) return
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
def send_database_report(event): """Send a message to Slack with details of the database pointings and visibility.""" title = ['*Visibility for event {}*'.format(event.name)] # Basic details details = [] filepath = None with db.open_session() as session: # Query Event table entries db_events = session.query( db.Event).filter(db.Event.name == event.name).all() details += [ 'Number of entries in the events table: {}'.format(len(db_events)) ] if len(db_events) == 0: # Uh-oh details += ['*ERROR: Nothing found in database*'] else: # This event should be the latest added db_event = db_events[-1] # Get Mpointings db_mpointings = db_event.mpointings details += [ 'Number of targets for this event: {}'.format( len(db_mpointings)) ] if len(db_mpointings) == 0: # It might be because it's a retraction, so we've removed the previous pointings if event.type == 'GW_RETRACTION': details += ['- Previous targets removed successfully'] # Or it might be because no tiles passed the filter elif (event.strategy['on_grid'] and event.strategy['prob_limit'] > 0 and max(event.full_table['prob']) < event.strategy['prob_limit']): details += [ '- No tiles passed the probability limit ' + '({:.1f}%, '.format(event.strategy['prob_limit'] * 100) + 'highest had {:.1f}%)'.format( max(event.full_table['prob']) * 100), ] else: # Uh-oh details += ['- *ERROR: No Mpointings found in database*'] else: # Get the Mpointing coordinates ras = [mpointing.ra for mpointing in db_mpointings] decs = [mpointing.dec for mpointing in db_mpointings] coords = SkyCoord(ras, decs, unit='deg') for site in ['La Palma']: # TODO: should be in params details += ['Predicted visibility from {}:'.format(site)] # Create Astroplan Observer observer = Observer.at_site(site.lower().replace(' ', '')) # Create visibility constraints min_alt = float( event.strategy['constraints_dict']['min_alt']) * u.deg max_sunalt = float(event.strategy['constraints_dict'] ['max_sunalt']) * u.deg alt_constraint = AltitudeConstraint(min=min_alt) night_constraint = AtNightConstraint( max_solar_altitude=max_sunalt) constraints = [alt_constraint, night_constraint] # Check visibility until the stop time start_time = event.strategy['start_time'] stop_time = event.strategy['stop_time'] details += [ '- Valid dates: {} to {}'.format( start_time.datetime.strftime('%Y-%m-%d'), stop_time.datetime.strftime('%Y-%m-%d')) ] if event.strategy['stop_time'] < Time.now(): # The Event pointings will have expired delta = Time.now() - event.strategy['stop_time'] details[-1] += ' _(expired {:.1f} days ago)_'.format( delta.to('day').value) mps_visible_mask = is_observable( constraints, observer, coords, time_range=[start_time, stop_time]) details += [ '- Targets visible during valid period: {}/{}'.format( sum(mps_visible_mask), len(db_mpointings)) ] if event.strategy['on_grid']: # Find the total probibility for all tiles mp_tiles = np.array( [mp.grid_tile.name for mp in db_mpointings]) total_prob = event.grid.get_probability( list(mp_tiles)) * 100 details += [ '- Total probability in all tiles: {:.1f}%'.format( total_prob) ] # Get visible mp tile names mp_tiles_visible = mp_tiles[mps_visible_mask] visible_prob = event.grid.get_probability( list(mp_tiles_visible)) * 100 details += [ '- Probability in visible tiles: {:.1f}%'.format( visible_prob) ] # Get non-visible mp tile names mps_notvisible_tonight_mask = np.invert( mps_visible_mask) mp_tiles_notvisible = mp_tiles[ mps_notvisible_tonight_mask] # Get all non-visible tiles tiles_visible_mask = is_observable( constraints, observer, event.grid.coords, time_range=[start_time, stop_time]) tiles_notvisible_mask = np.invert(tiles_visible_mask) tiles_notvisible = np.array( event.grid.tilenames)[tiles_notvisible_mask] # Create a plot of the tiles, showing visibility tonight # TODO: multiple sites? Need multiple plots or one combined? filename = event.name + '_tiles.png' filepath = os.path.join(params.FILE_PATH, filename) event.grid.plot( filename=filepath, plot_skymap=True, highlight=[mp_tiles_visible, mp_tiles_notvisible], highlight_color=['blue', 'red'], color={ tilename: '0.5' for tilename in tiles_notvisible }, ) message_text = '\n'.join(title + details) # Send the message, with the plot attached if one was generated send_slack_msg(message_text, filepath=filepath)