def prio_schedule(): for priority, bandpass in enumerate(['B', 'G', 'R']): b = ObservingBlock.from_exposures(deneb, priority, deneb_exp, n, read_out, configuration={'filter': bandpass}, constraints=[first_half_night]) blocks.append(b) b = ObservingBlock.from_exposures(m13, priority, m13_exp, n, read_out, configuration={'filter': bandpass}, constraints=[first_half_night]) blocks.append(b) slew_rate = .8 * u.deg / u.second # define how telescope transition between block transitioner = Transitioner( slew_rate, { 'filter': { ('B', 'G'): 10 * u.second, ('G', 'R'): 10 * u.second, 'default': 30 * u.second } }) # init new seq scheduler prior_scheduler = PriorityScheduler(constraints=global_constraints, observer=apo, transitioner=transitioner) #Initialize a Schedule object, to contain the new schedule priority_schedule = Schedule(noon_before, noon_after) prior_scheduler(blocks, priority_schedule) plt.figure(figsize=(14, 6)) plot_schedule_airmass(priority_schedule) plt.legend(loc="upper right") #plt.show() plt.savefig('prior_scheduler.png')
def observation_schedule( followup_requests, instrument, observation_start=Time.now(), observation_end=Time.now() + TimeDelta(12 * u.hour), output_format='csv', figsize=(10, 8), ): """Create a schedule to display observations for a particular instrument Parameters ---------- followup_requests : skyportal.models.followup_request.FollowupRequest The planned observations associated with the request instrument : skyportal.models.instrument.Instrument The instrument that the request is made based on observation_start: astropy.time.Time Start time for the observations observation_end: astropy.time.Time End time for the observations output_format : str, optional "csv", "pdf" or "png" -- determines the format of the returned observation plan figsize : tuple, optional Matplotlib figsize of the pdf/png created Returns ------- dict success : bool Whether the request was successful or not, returning a sensible error in 'reason' name : str suggested filename based on `instrument` and `output_format` data : str binary encoded data for the file (to be streamed) reason : str If not successful, a reason is returned. """ location = EarthLocation.from_geodetic( instrument.telescope.lon * u.deg, instrument.telescope.lat * u.deg, instrument.telescope.elevation * u.m, ) observer = Observer(location=location, name=instrument.name) global_constraints = [ AirmassConstraint(max=2.50, boolean_constraint=False), AtNightConstraint.twilight_civil(), MoonSeparationConstraint(min=10.0 * u.deg), ] blocks = [] read_out = 10.0 * u.s for ii, followup_request in enumerate(followup_requests): obj = followup_request.obj coord = SkyCoord(ra=obj.ra * u.deg, dec=obj.dec * u.deg) target = FixedTarget(coord=coord, name=obj.id) payload = followup_request.payload allocation = followup_request.allocation requester = followup_request.requester if "start_date" in payload: start_date = Time(payload["start_date"], format='isot') if start_date > observation_end: continue if "end_date" in payload: end_date = Time(payload["end_date"], format='isot') if end_date < observation_start: continue if "priority" in payload: priority = payload["priority"] else: priority = 1 # make sure to invert priority (priority = 5.0 is max, priority = 1.0 is min) priority = 5.0 / priority if "exposure_time" in payload: exposure_time = payload["exposure_time"] * u.s else: exposure_time = 3600 * u.s if "exposure_counts" in payload: exposure_counts = payload["exposure_counts"] else: exposure_counts = 1 # get extra constraints constraints = [] if "minimum_lunar_distance" in payload: constraints.append( MoonSeparationConstraint( min=payload['minimum_lunar_distance'] * u.deg)) if "maximum_airmass" in payload: constraints.append( AirmassConstraint(max=payload['maximum_airmass'], boolean_constraint=False)) if "observation_choices" in payload: configurations = [{ 'requester': requester.username, 'group_id': allocation.group_id, 'request_id': followup_request.id, 'filter': bandpass, } for bandpass in payload["observation_choices"]] else: configurations = [{ 'requester': requester.username, 'group_id': allocation.group_id, 'request_id': followup_request.id, 'filter': 'default', }] for configuration in configurations: b = ObservingBlock.from_exposures( target, priority, exposure_time, exposure_counts, read_out, configuration=configuration, ) blocks.append(b) # Initialize a transitioner object with the slew rate and/or the # duration of other transitions (e.g. filter changes) slew_rate = 2.0 * u.deg / u.second transitioner = Transitioner(slew_rate, {'filter': { 'default': 10 * u.second }}) # Initialize the sequential scheduler with the constraints and transitioner prior_scheduler = PriorityScheduler(constraints=global_constraints, observer=observer, transitioner=transitioner) # Initialize a Schedule object, to contain the new schedule priority_schedule = Schedule(observation_start, observation_end) # Call the schedule with the observing blocks and schedule to schedule the blocks prior_scheduler(blocks, priority_schedule) if output_format in ["png", "pdf"]: matplotlib.use("Agg") fig = plt.figure(figsize=figsize, constrained_layout=False) plot_schedule_airmass(priority_schedule, show_night=True) plt.legend(loc="upper right") buf = io.BytesIO() fig.savefig(buf, format=output_format) plt.close(fig) buf.seek(0) return { "success": True, "name": f"schedule_{instrument.name}.{output_format}", "data": buf.read(), "reason": "", } elif output_format == "csv": try: schedule_table = priority_schedule.to_table(show_transitions=False, show_unused=False) except Exception as e: raise ValueError( f'Scheduling failed: there are probably no observable targets: {str(e)}.' ) schedule = [] for block in schedule_table: target = block["target"] if target == "TransitionBlock": continue filt = block["configuration"]["filter"] request_id = block["configuration"]["request_id"] group_id = block["configuration"]["group_id"] requester = block["configuration"]["requester"] obs_start = Time(block["start time (UTC)"], format='iso') obs_end = Time(block["end time (UTC)"], format='iso') exposure_time = int(block["duration (minutes)"] * 60.0) c = SkyCoord(ra=block["ra"] * u.degree, dec=block["dec"] * u.degree, frame='icrs') ra = c.ra.to_string(unit=u.hour, sep=':') dec = c.dec.to_string(unit=u.degree, sep=':') observation = { 'request_id': request_id, 'group_id': group_id, 'object_id': target, 'ra': ra, 'dec': dec, 'epoch': 2000, 'observation_start': obs_start, 'observation_end': obs_end, 'exposure_time': exposure_time, 'filter': filt, 'requester': requester, } schedule.append(observation) df = pd.DataFrame(schedule) with tempfile.NamedTemporaryFile(mode='w', suffix='.' + output_format) as f: df.to_csv(f.name) f.flush() with open(f.name, mode='rb') as g: csv_content = g.read() return { "success": True, "name": f"schedule_{instrument.name}.{output_format}", "data": csv_content, "reason": "", }
def schedule(observations: List[Dict], session: Dict, program: Dict) -> List[ObservingBlock]: """ Return the next object to be imaged according to the 'general' scheduling algorithm, and the time that the executor must wait before imaging this observation. Parameters ---------- observations: List[Dict] A list of dictionaries representing the observations to be scheduled program: Dict The Program object containing the configuration of this observational program Returns ------- obs: Dict The next observation to be executed wait: int The time (in seconds) to wait before imaging this observation. Authors: rprechelt """ # create observer observatory = astroplan.Observer(latitude=config.general.latitude*units.deg, longitude=config.general.longitude*units.deg, elevation=config.general.altitude*units.m, name="Atlas", timezone="UTC") # build default constraints global_constraints = [constraints.AltitudeConstraint(min=40*units.deg), # set minimum altitude constraints.AtNightConstraint.twilight_nautical()] # sun below -18 # list to store observing blocks blocks = [] # create targets for observation in observations: # target coordinates center = SkyCoord(observation['RA']+' '+observation['Dec'], unit=(units.hourangle, units.deg)) # create and apply offset ra_offset = Angle(observation['options'].get('ra_offset') or 0, unit=units.arcsec) dec_offset = Angle(observation['options'].get('dec_offset') or 0, unit=units.arcsec) # offset coordinates coord = SkyCoord(center.ra + ra_offset, center.dec + dec_offset) # create astroplan traget target = FixedTarget(coord=coord, name=observation['target']) # priority - if the observation has a priority, otherwise 1 priority = observation['options'].get('priority') or 1 # create local constraints ltc = constraints.LocalTimeConstraint(min=datetime.datetime.now().time(), max=session['end'].time()) # if specified, restrict airmass, otherwise no airmass restriction max_airmass = observation['options'].get('airmass') or 38 airmass = constraints.AirmassConstraint(max=max_airmass, boolean_constraint = False)] # rank by airmass # if specified, use observations moon separation, otherwise use 2 degrees moon_sep = observation['options'].get('moon')*units.deg or 2*units.deg moon = constraints.MoonSeparationConstraint(min=moon_sep*units.deg), # time, airmass, moon, + altitude, and at night constraints = [ltc, airmass, moon] # create observing block for this target blocks.append(ObservingBlock.from_exposures(target, priority, observation['exposure_time']*units.second, observation['exposure_count']*len(observation['filters']), config.telescope.readout_time*units.second, configuration = observation, constraints = [ltc])) # we need to create a transitioner to go between blocks transitioner = astroplan.Transitioner(1*units.deg/units.second, {'filter': {'default': 3*units.second}})
boolean_constraint = False), AtNightConstraint.twilight_civil()] rise_time = kp.sun_rise_time(tstart, which=u'next', horizon=-12*u.deg).iso set_time = kp.sun_set_time(tstart, which=u'next', horizon=-12*u.deg).iso blocks = [] read_out = 10.0 * u.s nexp = 1 for ii, target in enumerate(targets): bandpass = target["filter"] exposure_time = int(target["exposure_time"]) * u.s priority = target["sig"] b = ObservingBlock.from_exposures(target["target"],priority, exposure_time, nexp, read_out, configuration = {'filter': bandpass}) blocks.append(b) # Initialize a transitioner object with the slew rate and/or the # duration of other transitions (e.g. filter changes) slew_rate = 2.0*u.deg/u.second transitioner = Transitioner(slew_rate, {'filter':{'default': 10*u.second}}) # Initialize the sequential scheduler with the constraints and transitioner prior_scheduler = PriorityScheduler(constraints = global_constraints, observer = kp, transitioner = transitioner) # Initialize a Schedule object, to contain the new schedule priority_schedule = Schedule(tstart, tend)
deneb_exp = 60 * u.second m13_exp = 100 * u.second n = 16 blocks = [] half_night_start = Time('2016-07-07 02:00') half_night_end = Time('2016-07-07 08:00') first_half_night = TimeConstraint(half_night_start, half_night_end) for priority, bandpass in enumerate(['B', 'G', 'R']): b = ObservingBlock.from_exposures(deneb, priority, deneb_exp, n, read_out, configuration={'filter': bandpass}, constraints=[first_half_night]) blocks.append(b) b = ObservingBlock.from_exposures(m13, priority, m13_exp, n, read_out, configuration={'filter': bandpass}, constraints=[first_half_night]) blocks.append(b) slew_rate = .8 * u.deg / u.second
def schedule(observations: List[Dict], session: Dict, program: Dict) -> List[ObservingBlock]: """ Return the next object to be imaged according to the 'general' scheduling algorithm, and the time that the executor must wait before imaging this observation. Parameters ---------- observations: List[Dict] A list of dictionaries representing the observations to be scheduled program: Dict The Program object containing the configuration of this observational program Returns ------- obs: Dict The next observation to be executed wait: int The time (in seconds) to wait before imaging this observation. Authors: rprechelt """ # create observer observatory = astroplan.Observer( latitude=config.general.latitude * units.deg, longitude=config.general.longitude * units.deg, elevation=config.general.altitude * units.m, name=config.general.name, timezone="UTC") # build default constraints global_constraints = [ constraints.AltitudeConstraint(min=config.telescope.min_alt * units.deg), # set minimum altitude constraints.AtNightConstraint.twilight_nautical(), constraints.TimeConstraint(min=Time(datetime.datetime.now()), max=Time(session['end'])) ] # list to store observing blocks blocks = [] # create targets for observation in observations: # check whether observation has RA and Dec values if observation.get('RA') is None: continue if observation.get('Dec') is None: continue # target coordinates center = SkyCoord(observation['RA'] + ' ' + observation['Dec'], unit=(units.hourangle, units.deg)) # create and apply offset ra_offset = Angle(observation['options'].get('ra_offset') or 0, unit=units.arcsec) dec_offset = Angle(observation['options'].get('dec_offset') or 0, unit=units.arcsec) # offset coordinates coord = SkyCoord(center.ra + ra_offset, center.dec + dec_offset) # create astroplan traget target = FixedTarget(coord=coord, name=observation['target']) # priority - if the observation has a priority, otherwise 1 priority = observation['options'].get('priority') or 1 # if specified, restrict airmass, otherwise no airmass restriction max_airmass = observation['options'].get('airmass') or 38 airmass = constraints.AirmassConstraint( max=max_airmass, boolean_constraint=False) # rank by airmass # if specified, use observations moon separation, otherwise use 2 degrees moon_sep = (observation['options'].get('moon') or config.queue.moon_separation) * units.deg moon = constraints.MoonSeparationConstraint(min=moon_sep) # time, airmass, moon, + altitude, and at night local_constraints = [moon] # create observing block for this target blocks.append( ObservingBlock.from_exposures( target, priority, observation['exposure_time'] * units.second, observation['exposure_count'] * (len(observation['filters']) + 1), config.telescope.readout_time * units.second, configuration=observation, constraints=local_constraints)) # we need to create a transitioner to go between blocks transitioner = astroplan.Transitioner( 1 * units.deg / units.second, {'filter': { 'default': 4 * units.second }}) # create priority scheduler priority_scheduler = scheduling.PriorityScheduler( constraints=global_constraints, observer=observatory, transitioner=transitioner) # initialize the schedule schedule = scheduling.Schedule(Time.now(), Time(session['end'])) # schedule! schedule = priority_scheduler(blocks, schedule) # print(schedule.to_table()) # print(f'observing_blocks: {schedule.observing_blocks}') # print(f'open_slots: {schedule.open_slots}') # print(f'scheduled_blocks: {schedule.scheduled_blocks}') # return the scheduled blocks return schedule
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 generate_schedule( telescope_config, global_constraint_configuration, start_datetime, end_datetime, no_weather_constraints=False, requests=None ): # Sometimes a warning arises called OldEarthOrientationDataWarning which means the following line must run to refresh # should find a way to catch this warning and only download when necessary download_IERS_A() cfht = Observer.at_site('cfht') transitioner = create_transitioner(telescope_config['slew_rate'], telescope_config['filters']) # Retrieve global constraints from Constraint Aggregator global_constraints = ca.initialize_constraints( global_constraint_configuration, start_datetime, end_datetime, no_weather_constraints ) # Currently defined in config but may belong on the block-level instead read_out = telescope_config['read_out'] * u.second # TODO: gather this data from each ObservationRequest instead n_exp = 5 blocks = [] # In order to supply block-level constraints, they should be initialized from attributes of the source data # and passed into the ObservingBlock initialization below for request in requests: # For each filter available on the telescope # TODO: define the available set within config for bandpass in ['B', 'G', 'R']: block = ObservingBlock.from_exposures( request.target, request.priority, request.duration, n_exp, read_out, configuration={'filter': bandpass}, constraints=[] ) blocks.append(block) print('Appending block {} with Target {} and filter {}'.format(block, request.target, bandpass)) prior_scheduler = PriorityScheduler( constraints=global_constraints, observer=cfht, transitioner=transitioner ) priority_schedule = Schedule(Time(start_datetime), Time(end_datetime)) prior_scheduler(blocks, priority_schedule) return priority_schedule