Source code for schedview.compute.astro
import datetime
from functools import cache
import numpy as np
import pandas as pd
import pytz
from astropy.time import Time
from rubin_scheduler.scheduler.model_observatory import ModelObservatory
from rubin_scheduler.site_models.almanac import Almanac
@cache
def _compute_all_night_events():
# Loading the alamac takes a while, so generate it with a function
# that caches the result (using the functools.cache decorator).
almanac = Almanac()
all_nights_events = pd.DataFrame(almanac.sunsets)
all_nights_events["night_middle"] = (all_nights_events["sunrise"] + all_nights_events["sunset"]) / 2
return all_nights_events
[docs]
@cache
def convert_evening_date_to_night_of_survey(night_date, timezone="Chile/Continental"):
"""Convert a calendar date in the evening to the night of survey.
Parameters
----------
night_date : `datetime.date`
The calendar date in the evening local time.
timezone: `str`
The string designating the time zone. Defaults to 'Chile/Continental'
Returns
-------
night_of_survey : `int`
The night of survey, starting from 0.
"""
sample_time = Time(
pytz.timezone(timezone)
.localize(datetime.datetime(night_date.year, night_date.month, night_date.day, 23, 59, 59))
.astimezone(pytz.timezone("UTC"))
)
all_nights_events = _compute_all_night_events()
closest_middle_iloc = np.abs(sample_time.mjd - all_nights_events["night_middle"]).argsort()[0]
night_of_survey = all_nights_events.iloc[closest_middle_iloc, :]["night"]
return night_of_survey
[docs]
def night_events(night_date=None, site=None, timezone="Chile/Continental"):
"""Creata a pandas.DataFrame with astronomical events.
Parameters
----------
night_date : `datetime.date`
The calendar date in the evening local time.
site : `astropy.coordinates.earth.EarthLocation`
The observatory location. Defaults to Rubin observatory.
timezone: `str`
The timezone name. Defaults to 'Chile/Continental'
Returns
-------
events : `pandas.DataFrame`
A DataFrame of night events.
"""
if night_date is None:
night_date = datetime.date.today()
if site is None:
site = ModelObservatory().location
all_nights_events = _compute_all_night_events().set_index("night")
night_of_survey = convert_evening_date_to_night_of_survey(night_date, timezone=timezone)
mjds = all_nights_events.loc[night_of_survey]
# Not all night have both a moon rise and moon set. If a night is missing
# one, use the value from the following night or prior night, whichever
# is closer to night_middle
for event in mjds.index:
if mjds[event] <= 0:
next_night = night_of_survey + 1
prior_night = night_of_survey - 1
night_middle = mjds["night_middle"]
next_dt = np.abs(all_nights_events.loc[next_night, event] - night_middle)
proir_dt = np.abs(all_nights_events.loc[prior_night, event] - night_middle)
closest_night = next_night if next_dt < proir_dt else prior_night
mjds[event] = all_nights_events.loc[closest_night, event]
ap_times = Time(mjds, format="mjd", scale="utc", location=site)
time_df = pd.DataFrame(
{
"MJD": ap_times.mjd,
"LST": ap_times.sidereal_time("apparent").deg,
"UTC": pd.to_datetime(ap_times.iso).tz_localize("UTC"),
},
index=mjds.index,
)
time_df[timezone] = time_df["UTC"].dt.tz_convert(timezone)
time_df.index.name = "event"
return time_df
def compute_central_night(visits, site=None, timezone="Chile/Continental"):
"""Compute the central night of a set of visits.
Parameters
----------
visits : `pandas.DataFrame`
A DataFrame of visits.
site : `astropy.coordinates.earth.EarthLocation`
The observatory location. Defaults to Rubin observatory.
timezone: `str`
The timezone name. Defaults to 'Chile/Continental'
Returns
-------
central_night : `datetime.date`
The central night of the visits.
"""
central_mjd = visits["observationStartMJD"].median()
candidate_night = Time(central_mjd, format="mjd", scale="utc").datetime.date()
# The mjd rollover can occur during the night, so the above might be offset
# by a night. Make sure the night we have is the one with a central mjd
# closest to the median visit mjd.
candidate_middle_mjd = night_events(candidate_night, site, timezone).loc["night_middle", "MJD"]
mjd_shift = np.round(central_mjd - candidate_middle_mjd)
central_night = Time(central_mjd + mjd_shift, format="mjd", scale="utc").datetime.date()
return central_night