Source code for rubin_scheduler.utils.sun_position

__all__ = ("NextTimeSun",)

import numpy as np
from astropy.coordinates import AltAz, EarthLocation, get_body, get_sun
from astropy.time import Time
from scipy.optimize import minimize

from rubin_scheduler.utils import Site


[docs] class NextTimeSun(object): """Find the next time the sun will be at some altitude. Could be done with astroplan, but trying to save a dependency. Note: Probably fails at extreem latitudes when the sun doesn't rise/set for many days at a time. Parameter --------- location : `astropy.coordinates.EarthLocation` Location of the observatory. Defaults to LSST. """ def __init__(self, location=None): if location is None: site = Site("LSST") self.location = EarthLocation(lat=site.latitude, lon=site.longitude, height=site.height) else: self.location = location self.frame = AltAz(location=self.location) def _call_to_min(self, mjd): """Method that can be used by minimization routine.""" return (self.alt_at_mjd(mjd) - self.altitude) ** 2
[docs] def alt_at_mjd(self, mjd): """Return sun altitude in degrees for given MJD.""" sun_altaz = get_sun(Time(mjd, format="mjd")).transform_to(self.frame) return sun_altaz.alt.deg
def sun_moon(self, mjd): sun = get_sun(Time(mjd, format="mjd")) sun_frame = sun.transform_to(self.frame) moon = get_body("moon", Time(mjd, format="mjd")) moon_frame = moon.transform_to(self.frame) return sun, sun_frame, moon, moon_frame
[docs] def next_mjd_at_alt( self, mjd, altitude=-12.0, rising=True, time_steps=20, forward_check_length=1.5, **kwargs ): """Find the time the sun will next be at a given altitude. Parameters ---------- mjd : `float` The modified Julian Date. altitude : `float` Altitude for the sun (Degrees). Default -12. rising : `bool` Should the sun be rising (True) or setting (False). Default True. time_steps : `int` How many time steps to use when finding next time. Default 20. forward_check_length : `float` How far into the future to look for the next sun positions. Default 1.5 (days) **kwargs Passed to scipy.optimize.minimize. Returns ------- Time sun is at expected positions as MJD """ self.altitude = altitude tsteps = np.linspace(0, forward_check_length, num=time_steps) times = Time(mjd + tsteps, format="mjd") sun_altaz = get_sun(times).transform_to(self.frame) # Get the slopes diff_limit = sun_altaz.alt.deg - altitude # If there is an amazing lucky strike and # we hit the exact floating point precicion time if 0 in diff_limit: return times[np.where(diff_limit == 0)].mjd rise_set_sign = np.diff(np.sign(diff_limit)) # Sun setting when ack == -2, rising when ack ==2 if rising: indx = np.min(np.where(rise_set_sign == 2)[0]) else: indx = np.min(np.where(rise_set_sign == -2)[0]) x0 = times[indx].mjd bounds = [(times[indx - 1].mjd, times[indx + 1].mjd)] result = minimize(self._call_to_min, x0, bounds=bounds, **kwargs) # take a max so we are sure to return a scalar. return np.max(result.x)