Source code for rubin_scheduler.scheduler.surveys.plan_night_survey

__all__ = ("PlanAheadSurvey",)

from copy import copy

import healpy as hp
import numpy as np

import rubin_scheduler.scheduler.basis_functions as bfs
from rubin_scheduler.scheduler import features

from .surveys import BlobSurvey


[docs] class PlanAheadSurvey(BlobSurvey): """Have a survey object that can plan ahead if it will want to observer a blob later in the night Parameters ---------- delta_mjd_tol : float The tolerance to alow on when to execute scheduled observations (days) minimum_sky_area : float The minimum sky area to demand before making a scheduled observation (square degrees) track_filters : str The filter name we want to prevent long gaps on in_season : float The distance in RA from the meridian at midnight to consider (hours). This is the half-width cadence : float Ignore gaps below this length (days) """ def __init__( self, basis_functions, basis_weights, delta_mjd_tol=0.3 / 24.0, minimum_sky_area=200.0, track_filters="g", in_season=2.5, cadence=9, **kwargs, ): super(PlanAheadSurvey, self).__init__(basis_functions, basis_weights, **kwargs) # note that self.night is already being used for tracking # tesselation. So here's an attribute for seeing if the night # has changed for cadence tracking self.night_cad = -100 self.scheduled_obs = None self.delta_mjd_tol = delta_mjd_tol self.minimum_sky_area = minimum_sky_area # sq degrees self.extra_features = {} self.extra_features["last_observed"] = features.Last_observed(filtername=track_filters) self.extra_basis_functions = {} self.extra_basis_functions["moon_mask"] = bfs.Moon_avoidance_basis_function() self.track_filters = track_filters self.in_season = in_season / 12.0 * np.pi # to radians self.pix_area = hp.nside2pixarea(self.nside, degrees=True) self.cadence = cadence def check_night(self, conditions): """""" delta_mjd = conditions.mjd - self.extra_features["last_observed"].feature moon_mask = self.extra_basis_functions["moon_mask"](conditions) pix_to_obs = np.where( (delta_mjd > self.cadence) & (np.abs(conditions.az_to_antisun) < self.in_season) & (moon_mask >= 0) )[0] area = np.size(pix_to_obs) * self.pix_area # If there are going to be some observations at a given time if area > self.minimum_sky_area: # Maybe just calculate the mean (with angles) # Via https://en.wikipedia.org/wiki/ # Mean_of_circular_quantities mean_ra = np.arctan2( np.mean(np.sin(conditions.ra[pix_to_obs])), np.mean(np.cos(conditions.ra[pix_to_obs])), ) if mean_ra < 0: mean_ra += 2.0 * np.pi hour_angle = conditions.lmst - mean_ra * 12.0 / np.pi # This should be running -12 hours to +12 hours hour_angle[np.where(hour_angle < -12)] += 24 hour_angle[np.where(hour_angle > 12)] -= 24 if hour_angle < 0: self.scheduled_obs = conditions.mjd - hour_angle / 24.0 else: self.scheduled_obs = conditions.mjd else: self.scheduled_obs = None
[docs] def calc_reward_function(self, conditions): # Only compute if we will want to observe sometime in the night self.reward = -np.inf if self._check_feasibility(conditions): if self.night_cad != conditions.night: self.check_night(conditions) self.night_cad = copy(conditions.night) if self.scheduled_obs is not None: # If there are scheduled observations, and we are in # the correct time window delta_mjd = conditions.mjd - self.scheduled_obs if (np.abs(delta_mjd) < self.delta_mjd_tol) & (self.scheduled_obs is not None): # So, we think there's a region that has had a # long gap and can be observed # call the standard reward function self.reward = super(PlanAheadSurvey, self).calc_reward_function(conditions) return self.reward
def generate_observations(self, conditions): observations = super(PlanAheadSurvey, self).generate_observations(conditions) # We are providing observations, so clear the scheduled obs # and reset the night so # self.check_night will get called again in case there's # another blob that should be done after this one completes self.scheduled_obs = None self.night_cad = conditions.night - 1 return observations