Source code for rubin_scheduler.utils.season_utils

__all__ = ("calc_season",)

import numpy as np
from astropy.coordinates import EarthLocation, get_sun
from astropy.time import Time

from .constants import SURVEY_START_MJD


[docs] def calc_season(ra, mjd, mjd_start=None, calc_diagonal=False): """Calculate the season (of visibility) in the survey for a series of ra/time values of an observation. Based only on the RA of the point on the sky, it calculates the 'season' based on when the sun passes through this RA (this marks the start of a 'season'). Parameters ---------- ra : `float` or `np.ndarray` (N,) The RA (in degrees) of the point(s) on the sky mjd : `np.ndarray`, (M,) The times of the observations, in MJD days mjd_start : `float`, optional The time of the start of the survey. Default None will use `rubin_scheduler.utils.survey_start_mjd()`. calc_diagonal : `bool`, optional If True AND if len(mjd) == len(ra), ONLY the 'diagonal' elements of the seasons will be calculated. That is: RA and mjd are assumed to be paired and the resulting seasons will be a 1-d `np.array` of dimension (N,). Returns ------- seasons : `np.array`, (M,) or `np.array` (N, M) The season values, as either a 1-d or 2-d array, depending on the value passed for ra and calc_diagonal. Notes ----- Seasons should be calculated using the RA of a fixed point on the sky, such as the slice_point['ra'] if calculating season values for a series of opsim pointings on the sky, rather than the value for each visit. To convert to integer seasons, use np.floor(seasons). "Season 0" will be the first (full) season after the RA value becomes visible for the first time after mjd_start. This induces a "discontinuity" in the resulting season values, corresponding to the RA where the survey started -- like the international dateline (in this case, where season goes from 'past season' to 'new season'). The scheduler `season_calc` routine computes "season" independently, and may have a 90 degree offset in the location of this 'skip'/zeropoint, plus requires different calculation of `offset` values to account for location on the sky. """ # Just to make it simpler to get simple values back try: len(ra) single_ra = False except TypeError: single_ra = True if mjd_start is None: mjd_start = SURVEY_START_MJD # A reference time and sun RA location to anchor the location of the Sun # This time was chosen as it is close to the expected start of the survey. # The time is an equinox point, so the sun is at dec=0. ref_time = 60940.0 ref_sun_ra = 178.98403541421598 # Calculate the fraction of the sphere/"year" for these RAs on the sky. offset = (ra - ref_sun_ra) / 360 * 365.25 # Turn the offsets into the (mjd) times that the season would start # at each RA value, in the reference year. season_began = ref_time + offset # Add an adjustment so that the first season (season = 0) # at each RA is in the year after mjd_start first = np.floor((season_began - mjd_start) / 365.25) season_began = season_began - first * 365.25 # Calculate the season value for each point. # season_began is an N length array, matching "ra" # mjds is an M length array, matching mjds (applied to every ra) if single_ra: seasons = (mjd - season_began) / 365.25 elif calc_diagonal and len(mjd) == len(ra): seasons = (mjd - season_began) / 365.25 else: seasons = np.zeros((len(ra), len(mjd)), dtype=float) for i, r in enumerate(ra): seasons[i] = (mjd - season_began[i]) / 365.25 return seasons
def _generate_reference(): # The reference values for calc_season can be evaluated using loc = EarthLocation.of_site("Rubin", refresh_cache=True) # time should ideally be near at equinox for RA projection considerations t = Time("2025-09-22T00:00:00.00", format="isot", scale="utc", location=loc) print("Ref time", t.utc.mjd) print("Ref sun RA", get_sun(t).ra.deg, t.utc.mjd) print("local sidereal time at time", t.sidereal_time("apparent").deg)