Scheduler Overview#
The scheduler in rubin_scheduler contains many classes which work in concert
to provide flexible, configurable algorithms for choosing observations, taking
into account the current weather conditions and observatory capabilities.
CoreScheduler#
In both actual operations and in simulations, the CoreScheduler does the work
of accepting telemetry of weather conditions and observatory state
(update_conditions()), and determining the best choice for the next
observation (or observations) (request_observation()) by polling its lists of Surveys .
It also accepts updates of completed observations (add_observation()) and
when desired, can flush its internal queue of awaiting observations
(flush_queue()).
The CoreScheduler is the primary interaction point when running the scheduler.
The CoreScheduler
itself however, does not determine what are useful or
desireable observations. That job belongs to the
Surveys.
While there could be only a
single Survey in the CoreScheduler, typically there are many, each
configured for different goals or methods of acquiring observations.
The Surveys are held in the CoreScheduler.survey_lists and are
organized into “tiers” – each tier contains a list of Surveys.
Each Survey object can check_feasibility() for a quick check on whether
current conditions match its requirements and calc_reward_function() for
the full calculation of the desirability of an observation under the
current conditions, based on the survey’s configuration.
Whenever CoreScheduler.request_observation() is called, the CoreScheduler
travels through each tier in survey_lists. Within each tier, the Survey
which is both feasible and has the greatest reward value will be chosen to
generate the next observation. If no Survey within a given tier is feasible,
the next tier will be queried for feasibility; this will continue
through the tiers until a Survey which is feasible is found. Thus
Surveys in the first tier have priority over Surveys in
later tiers.
Once a specific Survey is chosen to request an observation, the
Survey.generate_observations() method will be called. This provides
the specifics of the requested observation or observations. Many Surveys
will request a series of observations to be executed in sequence.
A step through the workflow of the CoreScheduler at a given time to generate an observation request looks like:
flowchart TD
A([Start]) ==>B[[Update Conditions]]
B ==> C[[Request Observation]]
C ==> D([Consider Surveys in Tier])
D --> E[Check Survey]
D --> F[Check Survey]
D --> G[Check Survey]
E --> H{Is any Survey in Tier feasible?}
F --> H
G --> H
H == Yes ==> J([Select Survey in Tier with highest reward])
H == No ==> I([Go to Next tier]) ==> D
J ==> K[Winning Survey Generates Observation]
K ==> L([End])
After an observation is acquired by the observatory
and CoreScheduler.add_observation() is
called to register the visit, the observation is also passed to each
individual Survey. Some Surveys will record this observation, while
others may ignore it, depending on their configurations.
Surveys#
The customization of survey strategy with rubin_scheduler happens in
the Survey objects, as well as in how they are placed into the tiers in the
CoreScheduler.survey_lists. There is a wide variety in how different
Survey objects behave or can be configured.
A Survey could be configured to observe pairs of visits at any point
over the sky (such as the BlobSurvey)
or it could be designed to simply
follow a scripted list of RA/Dec/Filter visits at pre-specified time windows
(such as a ScriptedSurvey).
Each Survey can check_feasibility(), which provides a quick check on
whether the current conditions meet the Survey requirements as well as
calc_reward_function(), which calculates the desirability of an
observation under the current conditions.
The calculation of the feasibility or reward for a given survey is governed
entirely by how these functions are implemented within the specific
Survey class.
A Survey is considered infeasible if check_feasibility() returns False.
It is also infeasible if the maximum final reward value is -Infinity.
The final reward value of a Survey is typically an array,
but can be a scalar in the case of Surveys defined only at a single point
(such as a FieldSurvey).
If chosen to generate an observation request, the Survey will return
either a single requested observation or a series of requested observations,
using generate_observations(). The specific choice of these observations
(such as whether this is a single visit or a series of visits)
is determined by the specific Survey’s implementation of this method.
The specifics of these requested observations include details added by its
Detailers
which add requested rotation angle or dithering positions, if
applicable.
Calculating Feasibility and Rewards#
The calculation of feasibility or reward is entirely dependant on how
these methods are implemented in the Survey class, and this can vary
depending on the intended use of the Survey.
A ScriptedSurvey or one derived from this class may just have a single constant reward value, but determine feasibility depending on whether any of its list of desired observations (which contain possible time windows) overlap the current time.
Most Surveys do contain a list of
BasisFunctions
which combine to calculate the overall reward for that Survey under
the current conditions. If so, each BasisFunction will also have
an associated weight; the total reward for the Survey is
simply the weighted sum of the BasisFunction values. These Surveys will
generally also have their own
Features, which
track relevant information for that Survey over time.
The BasisFunctions calculate values to contribute to the reward that
consider some aspect of the current conditions: a simple example is
the SlewtimeBasisFunction which calculates its value based on the slewtime
from the current telescope position to the desired location on the sky.
The Features track relevant information for that Survey,
such as how many observations have already been obtained or when the last
observation at a given pointing was acquired, and can be used by the
BasisFunctions for that Survey.
Typically Survey classes which are intended to be used for large areas of
sky contain BasisFunctions and inherit from the
BaseMarkovSurvey.
Most of the observations in the current baseline come from a Survey class
in this category, the
BlobSurvey.
Basis Functions#
For the Surveys which use BasisFunctions, the BasisFunctions
are where the list of “pros” and “cons” regarding obtaining observations under
the current conditions are calculated.
The final reward for these Surveys is the weighted sum of its
basis function values.
There are many different BasisFunctions available, and each can be configured
in different ways to generate different effects. Because they can be
configured in different ways, including keeping track of different
observations, BasisFunctions are not shared between Surveys.
Some examples of common BasisFunctions include:
classDiagram
BasisFunction <|-- Slewtime
BasisFunction <|-- M5Diff
BasisFunction <|-- Footprint
BasisFunction <|-- MoonAvoidance
BasisFunction <|-- FilterLoaded
BasisFunction : + Features
BasisFunction : check_feasibility()
BasisFunction : calc_value()
BasisFunction : add_observation()
BasisFunction : label()
class Slewtime{
+ Short Slews
}
class M5Diff{
+ Better depth
}
class Footprint{
+ Uniform coverage
}
class MoonAvoidance{
+ Avoid the Moon
}
class FilterLoaded{
+ Filter Available
}
The value of a given BasisFunction can be either a scalar or a map of the
sky (as healpix arrays). Generally, the
value returned depends on the type of BasisFunction, although this can also
be modified by the properties of the Survey (FieldSurveys, for example,
only consider the BasisFunction value at the location of their target).
Most commonly, BasisFunctions return a map if they are considering a property
that varies across the sky, such as M5DiffBasisFunction which tracks current
skybrightness compared to the best possible skybrightness in the specified
filter. If the BasisFunction returns a value of -Infinity, this will
be propagated through the weighted sum of BasisFunction values to the
Survey reward value. This is easiest to understand with avoidance zone
masks like the MoonAvoidanceBasisFunction or the
AvoidDirectWindBasisFunction which return -Infinity for the parts of the
sky which should be inaccessible for the telescope:
the -Infinity areas will be -Infinity in the Survey reward, and the
Survey will not request observations in these parts of the sky.
If multiple BasisFunctions in a Survey have regions of -Infinity,
it is possible for these regions to overlap in a way that makes the
final reward -Infinity at all points in the sky; this will make the
Survey infeasible under those conditions.
Sometimes a BasisFunction returns a scalar value, such as for the
FilterLoadedBasisFunction. This BasisFunction tracks whether the filter
for a desired observation is available in the camera filter wheel. If the
filter is available, it returns 0 which doesn’t modify the overall Survey
reward. If the filter is not available, it returns -Infinity, which
makes the Survey infeasible under those conditions.