diff --git a/docs/fbs-arch.rst b/docs/fbs-arch.rst index 6323bec7..1d83d52f 100644 --- a/docs/fbs-arch.rst +++ b/docs/fbs-arch.rst @@ -10,37 +10,85 @@ 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. + +Overview +^^^^^^^^ + In both actual operations and in simulations, the `CoreScheduler `_ does the work of accepting telemetry of weather conditions and observatory state -(`update_conditions()`) and then polling its lists of `Surveys `_ to determine the best -choice for the next observation (`request_observation()`). It also accepts -updates of completed observations (`add_observation()`) and when desired, -can flush its internal queue of awaiting observations (`flush_queue()`). +(`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`, once configured, is the primary interaction point for 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. These are -held in `CoreScheduler.survey_lists` and are organized into "tiers" -- +single `Survey` in the `CoreScheduler`, typically there are many, 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`. -A single 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 a given time (such as a `ScriptedSurvey`). 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 from its configuration, -under the current conditions. The calculation of the feasibility or reward -for a given survey is governed by its -`BasisFunctions `_ -and `Features `_. -The `BasisFunctions` contain the requirements and calculations for reward, -such accounting for slew time or the time since the last visit or the -expected limiting magnitude if an observation were to be acquired in the current -conditions, while 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. +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 tiers hold 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` +request more than one observation at the same time, once chosen). + +A step through the workflow of the CoreScheduler at a given time +to generate an observation request looks like: + +.. mermaid:: + + flowchart TD + A([Start]) ==>B[Update Conditions] + B ==> C[Request Observation] + C ==> D([Consider Surveys in Tier]) + D --> M( ) + M --> E[Check Survey] + M --> F[Check Survey] + M --> G[Check Survey] + E --> N( ) + F --> N + G --> N + N --> H{Is Any Survey Feasible?} + H == No? Next tier==> D + H == Yes? ==> I([Select Survey in Tier with highest reward]) + I ==> J[Winning Survey Generates Observation] + J ==> K([End]) + + +After an observation is acquired and `CoreScheduler.add_observation()` is +called, the observation is also passed to each individual `Survey`; some +`Surveys` will record this observation and add it into their `Features`, while +others may ignore it, depending on their configurations. + + +Surveys +^^^^^^^ + +The customization of survey strategy with `rubin_scheduler` happens in +the `Survey` objects and in how they are placed into the tiers in the +`CoreScheduler.survey_lists`, and there is a wide variety in `Survey` object +options. +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 a given time (such as a `ScriptedSurvey`). Some `Survey` types include: .. mermaid:: @@ -73,27 +121,42 @@ Some `Survey` types include: + Pairs } -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 -pick 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. - -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` -request more than one observation at the same time, once chosen). -In filling out the specifics of a requested observation, the `Survey` will -call on its -`Detailers `_) -to add information like requested rotation angle or dithering positions, if -applicable. +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 by its +`BasisFunctions `_ +and `Features `_. +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 a `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`. -After an observation is acquired and `CoreScheduler.add_observation()` is -called, the observation is also passed to each individual `Survey`; some -`Surveys` will record this observation and add it into their `Features`, while -others may ignore it, depending on their configurations. +Most `Survey` contain a list of `BasisFunctions`, which are combined to determine +the overall reward for that `Survey`. Each `BasisFunction` for a `Survey` has +an associated weight; the total reward for the `Survey` is simply the weighted +sum of the `BasisFunction` values. +Some `Surveys` do not use `BasisFunctions`. A `ScriptedSurvey`, for example, +might not have any `BasisFunctions`, just a list of desired observations and +time windows for those observations. The feasibility check in that case would +simply see if the current time matches the time window and if the observation +has already been acquired or not. +A `Survey` is considered infeasible if `check_feasibility()` returns False. +It is also infeasible if the 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 or a series of requested observations, +using `generate_observations()`. The specifics of these requested observations +include details added by its +`Detailers `_) +which can add requested rotation angle or dithering positions, if +applicable.