def test_observability_table(): subaru = Observer.at_site("Subaru") time_ranges = [ Time(["2001-02-03 04:05:06", "2001-02-04 04:05:06"]), # 1 day Time(["2007-08-09 10:11:12", "2007-08-09 11:11:12"]), ] # 1 hr targets = [vega, rigel, polaris] time_range = Time(["2001-02-03 04:05:06", "2001-02-04 04:05:06"]) # note that this uses the AirmassConstraint in None min mode - that means # targets below the horizon will pass the airmass constraint constraints = [AtNightConstraint(), AirmassConstraint(3, None)] obstab = observability_table(constraints, subaru, targets, time_range=time_range) assert len(obstab) == 3 assert np.all(obstab["target name"] == ["Vega", "Rigel", "Polaris"]) assert "times" in obstab.meta assert "observer" in obstab.meta assert "constraints" in obstab.meta assert len(obstab.meta["constraints"]) == 2 np.testing.assert_allclose(obstab["fraction of time observable"], np.array([21, 22, 15]) / 48) # now compare to is_observable and is_always_observable is_obs = is_observable(constraints, subaru, targets, time_range=time_range) np.testing.assert_allclose(obstab["ever observable"], is_obs) all_obs = is_always_observable(constraints, subaru, targets, time_range=time_range) np.testing.assert_allclose(obstab["always observable"], all_obs)
def test_observability_table(): subaru = Observer.at_site("Subaru") time_ranges = [Time(['2001-02-03 04:05:06', '2001-02-04 04:05:06']), # 1 day Time(['2007-08-09 10:11:12', '2007-08-09 11:11:12'])] # 1 hr targets = [vega, rigel, polaris] time_range = Time(['2001-02-03 04:05:06', '2001-02-04 04:05:06']) # note that this uses the AirmassConstraint in None min mode - that means # targets below the horizon will pass the airmass constraint constraints = [AtNightConstraint(), AirmassConstraint(3, None)] obstab = observability_table(constraints, subaru, targets, time_range=time_range) assert len(obstab) == 3 assert np.all(obstab['target name'] == ['Vega', 'Rigel', 'Polaris']) assert 'times' in obstab.meta assert 'observer' in obstab.meta assert 'constraints' in obstab.meta assert len(obstab.meta['constraints']) == 2 np.testing.assert_allclose(obstab['fraction of time observable'], np.array([21, 22, 15])/48) # now compare to is_observable and is_always_observable is_obs = is_observable(constraints, subaru, targets, time_range=time_range) np.testing.assert_allclose(obstab['ever observable'], is_obs) all_obs = is_always_observable(constraints, subaru, targets, time_range=time_range) np.testing.assert_allclose(obstab['always observable'], all_obs)
def check_observability(alerts, constraints, observer, targets, earliestObs, latestObs): """ Check observability from observatory given the observational constraints. Parameters ---------- alerts : pandas DataFrame table containing alerts constraints : list list of constraints observer : astroplan Observer object e.g. Calar Alto Observatory, Spain targets : list list containing astroplan targets earliestObs : Time object Earliest time of observation latestObs : Time object Latest time of observation Returns ------- alerts : pandas DataFrame filtered alerts table containing observable targets """ observabilityMask = is_observable(constraints, observer, targets, time_range=[earliestObs, latestObs]) return alerts[observabilityMask]
def test_docs_example(): # Test the example in astroplan/docs/tutorials/constraints.rst target_table_string = """# name ra_degrees dec_degrees Polaris 37.95456067 89.26410897 Vega 279.234734787 38.783688956 Albireo 292.68033548 27.959680072 Algol 47.042218553 40.955646675 Rigel 78.634467067 -8.201638365 Regulus 152.092962438 11.967208776""" from astroplan import Observer, FixedTarget from astropy.time import Time subaru = Observer.at_site("Subaru") time_range = Time(["2015-08-01 06:00", "2015-08-01 12:00"]) # Read in the table of targets from astropy.io import ascii target_table = ascii.read(target_table_string) # Create astroplan.FixedTarget objects for each one in the table from astropy.coordinates import SkyCoord import astropy.units as u targets = [FixedTarget(coord=SkyCoord(ra=ra*u.deg, dec=dec*u.deg), name=name) for name, ra, dec in target_table] from astroplan import Constraint, is_observable class VegaSeparationConstraint(Constraint): """ Constraint the separation from Vega """ def __init__(self, min=None, max=None): """ min : `~astropy.units.Quantity` or `None` (optional) Minimum acceptable separation between Vega and target. `None` indicates no limit. max : `~astropy.units.Quantity` or `None` (optional) Minimum acceptable separation between Vega and target. `None` indicates no limit. """ self.min = min if min else 0*u.deg self.max = max if max else 180*u.deg def compute_constraint(self, times, observer, targets): vega = SkyCoord(ra=279.23473479*u.deg, dec=38.78368896*u.deg) # Calculate separation between target and vega # Targets are automatically converted to SkyCoord objects # by __call__ before compute_constraint is called. vega_separation = vega.separation(targets) # Return an array that is True where the target is observable and # False where it is not return (self.min < vega_separation) & (vega_separation < self.max) constraints = [VegaSeparationConstraint(min=5*u.deg, max=30*u.deg)] observability = is_observable(constraints, subaru, targets, time_range=time_range) assert all(observability == [False, False, True, False, False, False])
def get_next_observable_target(self, target_list, obs_time, max_time=-1, airmass=(1, 2.2), moon_sep=(30, 180), block_type=''): """ :return: """ # If the target_list is empty then all we can do is return back no # target and do a standard for the time being next_target = False if target_list.empty: return next_target, "" for row in target_list.itertuples(): constraint = astroplan.AirmassConstraint(min=airmass[0], max=airmass[1]) if row.typedesig == 'f': if astroplan.is_observable(constraint, self.obs_site, row.FixedObject, times=[obs_time]): return_dict = {'name': row.objname, 'priority': row.priority} return row.req_id, {**row.obs_dict, **return_dict} return False, False
def test_observability_table(): subaru = Observer.at_site("Subaru") time_ranges = [ Time(['2001-02-03 04:05:06', '2001-02-04 04:05:06']), # 1 day Time(['2007-08-09 10:11:12', '2007-08-09 11:11:12']) ] # 1 hr targets = [vega, rigel, polaris] time_range = Time(['2001-02-03 04:05:06', '2001-02-04 04:05:06']) constraints = [AtNightConstraint(), AirmassConstraint(3)] obstab = observability_table(constraints, subaru, targets, time_range=time_range) assert len(obstab) == 3 assert np.all(obstab['target name'] == ['Vega', 'Rigel', 'Polaris']) assert 'times' in obstab.meta assert 'observer' in obstab.meta assert 'constraints' in obstab.meta assert len(obstab.meta['constraints']) == 2 np.testing.assert_allclose(obstab['fraction of time observable'], np.array([21, 22, 15]) / 48) #now compare to is_observable and is_always_observable is_obs = is_observable(constraints, subaru, targets, time_range=time_range) np.testing.assert_allclose(obstab['ever observable'], is_obs) all_obs = is_always_observable(constraints, subaru, targets, time_range=time_range) np.testing.assert_allclose(obstab['always observable'], all_obs)
def filter_targets(obs, i, target_list, date_time, roof=2.5): """ A function that filters a list of targets and returns a list with airmass less than < 2.5. TO IMPLEMENT: Move the testing to a different file or into a different function Args: obs: List of Observer objects that represent observatories target_list: List of FixedTarget objects that represent target coords date_time: The date and time range over which we check for observability roof: Float defining the maximum airmass we'll allow for our targets; will exclude targets with airmass larger than 2.5 Returns: None """ targets = format_targets(target_list) observers = format_observatories(obs) airmass_max = AirmassConstraint(roof) observability = is_observable(airmass_max, observers[i], targets, date_time) print(observability) print("Success!!")
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 objVis(self): """ Checks if transit is in between twilight hours. If not return error Returns the amount of time the object is above given airmass """ objVis = is_observable(self.constraints, self.observer, self.target, time_range=(self.discDate, self.discDate + 1 * u.day))[0] return objVis
def test_observability_table(): subaru = Observer.at_site("Subaru") time_ranges = [ Time(['2001-02-03 04:05:06', '2001-02-04 04:05:06']), # 1 day Time(['2007-08-09 10:11:12', '2007-08-09 11:11:12']) ] # 1 hr targets = [vega, rigel, polaris] time_range = Time(['2001-02-03 04:05:06', '2001-02-04 04:05:06']) # note that this uses the AirmassConstraint in None min mode - that means # targets below the horizon will pass the airmass constraint constraints = [AtNightConstraint(), AirmassConstraint(3, None)] obstab = observability_table(constraints, subaru, targets, time_range=time_range) assert len(obstab) == 3 assert np.all(obstab['target name'] == ['Vega', 'Rigel', 'Polaris']) assert 'times' in obstab.meta assert 'observer' in obstab.meta assert 'constraints' in obstab.meta assert len(obstab.meta['constraints']) == 2 np.testing.assert_allclose(obstab['fraction of time observable'], np.array([21, 22, 15]) / 48) # now compare to is_observable and is_always_observable is_obs = is_observable(constraints, subaru, targets, time_range=time_range) np.testing.assert_allclose(obstab['ever observable'], is_obs) all_obs = is_always_observable(constraints, subaru, targets, time_range=time_range) np.testing.assert_allclose(obstab['always observable'], all_obs) # try the scalar time_range case ttab = observability_table(constraints, subaru, targets, time_range=(time_range[0] - 12 * u.hour, time_range[0] + 12 * u.hour)) stab = observability_table(constraints, subaru, targets, time_range=time_range[0]) assert all(ttab['fraction of time observable'] == stab['fraction of time observable']) assert 'time observable' in stab.colnames
def test_at_night_basic(): subaru = Observer.at_site("Subaru") time_ranges = [Time(['2001-02-03 04:05:06', '2001-02-04 04:05:06']), # 1 day Time(['2007-08-09 10:11:12', '2007-08-09 11:11:12'])] # 1 hr targets = [vega, rigel, polaris] for time_range in time_ranges: # Calculate constraint using methods on astroplan.Observer: observer_is_night = subaru.is_night(time_grid_from_range(time_range)) observer_is_night_any = any(observer_is_night) observer_is_night_all = all(observer_is_night) assert all(is_observable(AtNightConstraint(), subaru, targets, time_range=time_range) == len(targets)*[observer_is_night_any]) assert all(is_always_observable(AtNightConstraint(), subaru, targets, time_range=time_range) == len(targets)*[observer_is_night_all])
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))
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') def test_coords(self): self.assertEqual(v_coords.ra, v_test_target[1]) self.assertEqual(v_coords.dec, v_test_target[2])
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' )
def get_obs_data(target, observers, current_time, alt_limit=30): """Compile infomation about the target's visibility from the given observers.""" all_data = {} if target is None: return all_data for observer in observers: data = {} data['observer'] = observer data['current_time'] = current_time # Get midnight and astronomical twilight times midnight = observer.midnight(current_time, which='next') sun_set = observer.twilight_evening_astronomical(midnight, which='previous') sun_rise = observer.twilight_morning_astronomical(midnight, which='next') dark_time = Time([sun_set, sun_rise]) data['midnight'] = midnight data['sun_set'] = sun_set data['sun_rise'] = sun_rise # Apply a constraint on altitude min_alt = alt_limit * u.deg alt_constraint = AltitudeConstraint(min=min_alt, max=None) alt_observable = is_observable(alt_constraint, observer, target, time_range=dark_time)[0] data['alt_constraint'] = alt_constraint data['alt_observable'] = alt_observable # Get target rise and set times if alt_observable: with warnings.catch_warnings(): warnings.simplefilter('ignore') target_rise = observer.target_rise_time(midnight, target, which='nearest', horizon=min_alt) target_set = observer.target_set_time(target_rise, target, which='next', horizon=min_alt) # Get observation times observation_start = target_rise observation_end = target_set if target_rise.jd < 0 or target_set.jd < 0: # target is always above the horizon, so visible all night observation_start = sun_set observation_end = sun_rise if target_rise < sun_set: # target is already up when the sun sets observation_start = sun_set if target_set > sun_rise: # target sets after the sun rises observation_end = sun_rise data['target_rise'] = target_rise data['target_set'] = target_set data['observation_start'] = observation_start data['observation_end'] = observation_end else: data['target_rise'] = None data['target_set'] = None data['observation_start'] = None data['observation_end'] = None # Apply a constraint on distance from the Moon min_moon = 5 * u.deg moon_constraint = MoonSeparationConstraint(min=min_moon, max=None) moon_observable = is_observable(moon_constraint, observer, target, time_range=dark_time)[0] data['moon_constraint'] = moon_constraint data['moon_observable'] = moon_observable all_data[observer.name] = data return all_data
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 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
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()
# first we define the conditions from astroplan import TimeWindow, AltitudeWindow, AboveAirmass, is_observable # times can be passed in as strings (interpreted as for get_date()) or # as astropy Time or datetime objects constraint_list = [TimeWindow("2015-05-01 18:30", "2015-05-02 05:30"), AltitudeWindow(15.0*u.deg, 89.0*u.deg), AboveAirmass(1.2)] # (AboveAirmass will be a subclass of AltitudeWindow) # Define a target tgt = FixedTarget(name='S5', ra='14:20:00.00', dec='48:00:00.00') # Combine a list of constraints to run on Observer, FixedTarget, and time to # determine the observability of target constraints = is_observable(constraint_list, obs, tgt, time_obs) # Test only a single constraint: constraints = is_observable(AboveAirmass(1.2), obs, tgt, time_obs) # `constraints` will be a boolean where True=observable. For a list of # targets, observatories, or times, `constraints` may be a booleans array # We will eventually need a more complicated method that minimizes a cost # function when optimizing an observing schedule given the results of # `is_observable`. # ====================================================== # Other useful calculations wrt an observer and a target #=======================================================
# convert everything to UTC start_time_utc = mst2utc(start_time_mst) end_time_utc = mst2utc(end_time_mst) observable_time_utc = mst2utc(observable_time_mst) time_range = Time([start_time_utc, end_time_utc]) # now we figure out what is observable at all with some simple constraints constraint = [ AtNightConstraint.twilight_civil(), AirmassConstraint(max=2.5, boolean_constraint=True) ] observable = astroplan.is_observable(constraint, kpno, targets, time_range=time_range) data['Observable'] = observable data.to_csv('updated.csv') if 0: # now we make the exposure times exp_time_single = 12 * u.second readout_time = 1.7 * u.second n_coadd = 5 n_dither = 12 exp_time_tot = n_coadd * n_dither * exp_time_single + readout_time no_optical_data = pd.isnull(data['Optical run1'])
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)
# first we define the conditions from astroplan import TimeRange, AltitudeRange, AirmassRange, is_observable # `is_observable` is a temporary function which will eventually be a method of # something to support caching # Times in TimeRange can be passed in as strings, will be passed to the # Time constructor. constraint_list = [TimeRange("2015-05-01 18:30", "2015-05-02 05:30"), AirmassRange(1.2), AltitudeRange(15.0*u.deg, 89.0*u.deg)] # (AboveAirmass will be a subclass of AltitudeWindow) # Combine a list of constraints to run on Observer, FixedTarget, and time to # determine the observability of target constraints = is_observable(constraint_list, obs, t1, time_obs) # Test only a single constraint: constraints = is_observable(AirmassRange(1.2), obs, t1, time_obs) # AirmassRange can accept two bounding airmasses, assumes single argument is # an upper limit, lower limit = 1. # `constraints` will be a boolean where True=observable. For a list of # targets, observatories, or times, `constraints` may be a booleans array # We will eventually need a more complicated method that minimizes a cost # function when optimizing an observing schedule given the results of # `is_observable`. # ======================================================
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 test_docs_example(): # Test the example in astroplan/docs/tutorials/constraints.rst target_table_string = """# name ra_degrees dec_degrees Polaris 37.95456067 89.26410897 Vega 279.234734787 38.783688956 Albireo 292.68033548 27.959680072 Algol 47.042218553 40.955646675 Rigel 78.634467067 -8.201638365 Regulus 152.092962438 11.967208776""" from astroplan import Observer, FixedTarget from astropy.time import Time subaru = Observer.at_site("Subaru") time_range = Time(["2015-08-01 06:00", "2015-08-01 12:00"]) # Read in the table of targets from astropy.io import ascii target_table = ascii.read(target_table_string) # Create astroplan.FixedTarget objects for each one in the table from astropy.coordinates import SkyCoord import astropy.units as u targets = [ FixedTarget(coord=SkyCoord(ra=ra * u.deg, dec=dec * u.deg), name=name) for name, ra, dec in target_table ] from astroplan import Constraint, is_observable from astropy.coordinates import Angle class VegaSeparationConstraint(Constraint): """ Constraint the separation from Vega """ def __init__(self, min=None, max=None): """ min : `~astropy.units.Quantity` or `None` (optional) Minimum acceptable separation between Vega and target. `None` indicates no limit. max : `~astropy.units.Quantity` or `None` (optional) Minimum acceptable separation between Vega and target. `None` indicates no limit. """ self.min = min self.max = max def compute_constraint(self, times, observer, targets): # Vega's coordinate must be non-scalar for the dimensions # to work out properly when combined with other constraints which # test multiple times vega = SkyCoord(ra=[279.23473479] * u.deg, dec=[38.78368896] * u.deg) # Calculate separation between target and vega vega_separation = Angle( [vega.separation(target.coord) for target in targets]) # If a maximum is specified but no minimum if self.min is None and self.max is not None: mask = vega_separation < self.max # If a minimum is specified but no maximum elif self.max is None and self.min is not None: mask = self.min < vega_separation # If both a minimum and a maximum are specified elif self.min is not None and self.max is not None: mask = ((self.min < vega_separation) & (vega_separation < self.max)) # Otherwise, raise an error else: raise ValueError("No max and/or min specified in " "VegaSeparationConstraint.") # Return an array that is True where the target is observable and # False where it is not return mask constraints = [VegaSeparationConstraint(min=5 * u.deg, max=30 * u.deg)] observability = is_observable(constraints, subaru, targets, time_range=time_range) assert all(observability == [False, False, True, False, False, False])
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
obstime = midnight + delta_midnight paranal = astroplan.Observer(paranal_loc, timezone='Etc/GMT-4') Altcons = astroplan.AltitudeConstraint(min=+30 * u.deg, max=None) Airmasscons = astroplan.AirmassConstraint(min=None, max=3.0) Alt_constraints = Altcons.compute_constraint(times=obstime, observer=paranal, targets=GJ9827b) Airmass_constraints = Airmasscons.compute_constraint(times=obstime, observer=paranal, targets=GJ9827b) observable = astroplan.is_observable(constraints=[Altcons, Airmasscons], observer=paranal, targets=GJ9827b, times=obstime) midnight1 = Time('2020-7-13 00:00:00') - utcoffset midnight2 = Time('2021-1-13 00:00:00') - utcoffset frame_July13night1 = AltAz(obstime=midnight1 + delta_midnight, location=paranal_loc) frame_July13night2 = AltAz(obstime=midnight2 + delta_midnight, location=paranal_loc) GJ9827baltazs_July13night1 = GJ9827b.transform_to(frame_July13night1) GJ9827baltazs_July13night2 = GJ9827b.transform_to(frame_July13night2) GJ9827bairmasss_July13night = GJ9827baltazs_July13night1.secz plt.plot(delta_midnight, GJ9827bairmasss_July13night)
print("Target: ", target.name) if False: print("Posang: ", target.posang) # constraints = [AltitudeConstraint(10*u.deg, 80*u.deg), # AzimuthConstraint(0*u.deg, 180*u.deg), # AirmassConstraint(5), AtNightConstraint.twilight_civil()] constraints = [AltitudeConstraint(10*u.deg, 80*u.deg), AirmassConstraint(5), AzimuthConstraint(0*u.deg, 10*u.deg), AtNightConstraint.twilight_civil()] from astroplan import is_observable, is_always_observable, months_observable # Are targets *ever* observable in the time range? ever_observable = is_observable(constraints, subaru, targets, time_range=time_range) # Are targets *always* observable in the time range? always_observable = is_always_observable(constraints, subaru, targets, time_range=time_range) # During what months are the targets ever observable? # best_months = months_observable(constraints, subaru, targets) import numpy as np observability_table = Table() observability_table['targets'] = [target.name for target in targets] observability_table['ever_observable'] = ever_observable observability_table['always_observable'] = always_observable print(observability_table)
def test_docs_example(): # Test the example in astroplan/docs/tutorials/constraints.rst target_table_string = """# name ra_degrees dec_degrees Polaris 37.95456067 89.26410897 Vega 279.234734787 38.783688956 Albireo 292.68033548 27.959680072 Algol 47.042218553 40.955646675 Rigel 78.634467067 -8.201638365 Regulus 152.092962438 11.967208776""" from astroplan import Observer, FixedTarget from astropy.time import Time subaru = Observer.at_site("Subaru") time_range = Time(["2015-08-01 06:00", "2015-08-01 12:00"]) # Read in the table of targets from astropy.io import ascii target_table = ascii.read(target_table_string) # Create astroplan.FixedTarget objects for each one in the table from astropy.coordinates import SkyCoord import astropy.units as u targets = [FixedTarget(coord=SkyCoord(ra=ra * u.deg, dec=dec * u.deg), name=name) for name, ra, dec in target_table] from astroplan import Constraint, is_observable from astropy.coordinates import Angle class VegaSeparationConstraint(Constraint): """ Constraint the separation from Vega """ def __init__(self, min=None, max=None): """ min : `~astropy.units.Quantity` or `None` (optional) Minimum acceptable separation between Vega and target. `None` indicates no limit. max : `~astropy.units.Quantity` or `None` (optional) Minimum acceptable separation between Vega and target. `None` indicates no limit. """ self.min = min self.max = max def compute_constraint(self, times, observer, targets): # Vega's coordinate must be non-scalar for the dimensions # to work out properly when combined with other constraints which # test multiple times vega = SkyCoord(ra=[279.23473479] * u.deg, dec=[38.78368896] * u.deg) # Calculate separation between target and vega vega_separation = Angle([vega.separation(target.coord) for target in targets]) # If a maximum is specified but no minimum if self.min is None and self.max is not None: mask = vega_separation < self.max # If a minimum is specified but no maximum elif self.max is None and self.min is not None: mask = self.min < vega_separation # If both a minimum and a maximum are specified elif self.min is not None and self.max is not None: mask = (self.min < vega_separation) & (vega_separation < self.max) # Otherwise, raise an error else: raise ValueError("No max and/or min specified in " "VegaSeparationConstraint.") # Return an array that is True where the target is observable and # False where it is not return mask constraints = [VegaSeparationConstraint(min=5 * u.deg, max=30 * u.deg)] observability = is_observable(constraints, subaru, targets, time_range=time_range) assert all(observability == [False, False, True, False, False, False])
def create_selection_coumpound_list(session_plan, schedule_form, observer, observation_time, time_from, time_to, tz_info, page, offset, per_page, sort_by, mag_scale, sort_def): global rise_set_cache if session_plan.is_anonymous and (schedule_form.obj_source.data is None or schedule_form.obj_source.data == 'WL'): schedule_form.obj_source.data = 'M' # set Messier if schedule_form.obj_source.data is None or schedule_form.obj_source.data == 'WL': wishlist_subquery = db.session.query(WishListItem.dso_id) \ .join(WishListItem.wish_list) \ .filter(WishList.user_id==current_user.id) dso_query = DeepskyObject.query \ .filter(DeepskyObject.id.in_(wishlist_subquery)) elif schedule_form.obj_source.data.startswith('DL_'): dso_list_id = int(schedule_form.obj_source.data[3:]) dsolist_subquery = db.session.query(DsoListItem.dso_id) \ .join(DsoListItem.dso_list) \ .filter(DsoList.id==dso_list_id) dso_query = DeepskyObject.query \ .filter(DeepskyObject.id.in_(dsolist_subquery)) else: dso_query = DeepskyObject.query cat_id = Catalogue.get_catalogue_id_by_cat_code(schedule_form.obj_source.data) if cat_id: dso_query = dso_query.filter_by(catalogue_id=cat_id) scheduled_subquery = db.session.query(SessionPlanItem.dso_id) \ .filter(SessionPlanItem.session_plan_id==session_plan.id) # Subtract already scheduled dsos dso_query = dso_query.filter(DeepskyObject.id.notin_(scheduled_subquery)) # Subtract observed dsos if not session_plan.is_anonymous and schedule_form.not_observed.data: observed_subquery = db.session.query(ObservedListItem.dso_id) \ .join(ObservedListItem.observed_list) \ .filter(ObservedList.user_id==current_user.id) dso_query = dso_query.filter(DeepskyObject.id.notin_(observed_subquery)) dso_query = dso_query.filter(or_(DeepskyObject.master_id.is_(None), DeepskyObject.master_id.notin_(observed_subquery))) # filter by type if schedule_form.dso_type.data and schedule_form.dso_type.data != 'All': dso_query = dso_query.filter(DeepskyObject.type==schedule_form.dso_type.data) # filter by magnitude limit if schedule_form.maglim.data is not None and schedule_form.maglim.data < mag_scale[1]: dso_query = dso_query.filter(DeepskyObject.mag<schedule_form.maglim.data) # filter by constellation if schedule_form.constellation_id.data is not None: dso_query = dso_query.filter(DeepskyObject.constellation_id==schedule_form.constellation_id.data) order_by_field = None if sort_by: desc = sort_by[0] == '-' sort_by_name = sort_by[1:] if desc else sort_by order_by_field = sort_def.get(sort_by_name) if order_by_field and desc: order_by_field = order_by_field.desc() if order_by_field is None: order_by_field = DeepskyObject.id all_count = dso_query.count() if all_count > 500: selection_list = dso_query.order_by(order_by_field).limit(per_page).offset(offset).all().copy() use_time_filter = False else: selection_list = dso_query.order_by(order_by_field).all().copy() use_time_filter = True # filter by rise-set time if use_time_filter: key_suffix = '/' + str(observer.location.lat) + '/' + str(observer.location.lon) + '/' + observation_time.strftime('%Y-%m-%d') index_table = [] i = 0 composed_selection_rms_list = [] to_process_list = [] for x in selection_list: key = str(x.id) + key_suffix cached = rise_set_cache.get(key, None) if cached is None: index_table.append(i) composed_selection_rms_list.append(None) to_process_list.append((x.ra, x.dec)) else: composed_selection_rms_list.append(cached) i += 1 if to_process_list: selection_rms_list = rise_merid_set_up(time_from, time_to, observer, to_process_list) for i in range(len(selection_rms_list)): index = index_table[i] val = selection_rms_list[i] composed_selection_rms_list[index] = val key = str(selection_list[index].id) + key_suffix rise_set_cache[key] = val time_filtered_list = [] i = 0 for rise_t, merid_t, set_t, is_up in composed_selection_rms_list: if is_up or rise_t < time_to or set_t>time_from: time_filtered_list.append((selection_list[i], _to_HM_format(rise_t, tz_info), _to_HM_format(merid_t, tz_info), _to_HM_format(set_t, tz_info))) i += 1 # filter by altitude if len(time_filtered_list) > 0 and schedule_form.min_altitude.data is not None and schedule_form.min_altitude.data > 0: constraints = [AltitudeConstraint(schedule_form.min_altitude.data*u.deg)] targets = [] for item in time_filtered_list: dso = item[0] target = FixedTarget(coord=SkyCoord(ra=dso.ra * u.rad, dec=dso.dec * u.rad), name=dso.name) targets.append(target) time_range = Time([time_from, time_to]) observable_list = is_observable(constraints, observer, targets, time_range=time_range) time_filtered_list = [ time_filtered_list[i] for i in range(len(time_filtered_list)) if observable_list[i] ] all_count = len(time_filtered_list) if offset>=all_count: offset = 0 page = 1 selection_compound_list = time_filtered_list[offset:offset+per_page] else: selection_rms_list = rise_merid_set_time_str(observation_time, observer, [ (x.ra, x.dec) for x in selection_list], tz_info) selection_compound_list = [ (selection_list[i], *selection_rms_list[i]) for i in range(len(selection_list))] return selection_compound_list, page, all_count