Source code for rubin_scheduler.scheduler.model_observatory.bright_observatory

__all__ = ("BrightObservatoryModel",)

import numpy as np
from astropy.time import Time

from rubin_scheduler.utils import SURVEY_START_MJD, NextTimeSun, _angular_separation

from .model_observatory import ModelObservatory


[docs] class BrightObservatoryModel(ModelObservatory): """Observatory that can go beyond sun altitude of -12 deg. Parameters ---------- sun_rise_limit_deg : `float` The sun altitude limit where we should skip ahead to the next sunset. Default -9 (degrees). sun_set_limit_deg : `float` What should the altitude of the sun be at sunset when observing starts. Default -9 (degrees). delta_time_step_sec : `float` A fudge time to make sure the sun is really higher than sun_set_limit_deg. Default 0.1 (seconds). **kwargs The usual ModelObservatory kwargs that get passed along. """ def __init__( self, mjd_start=SURVEY_START_MJD, mjd=None, sun_rise_limit_deg=-9, sun_set_limit_deg=-9, delta_time_step_sec=0.1, **kwargs, ): if mjd is None: mjd = mjd_start super().__init__(**kwargs) self.sun_rise_limit_deg = sun_rise_limit_deg self.sun_set_limit_deg = sun_set_limit_deg self.delta_time_step = delta_time_step_sec / 3600 / 24 # to days self.sun_moon_lookup = NextTimeSun(location=self.location) self.set_initial_mjd(mjd)
[docs] def add_sun_moon_data(self, observation): """Add info about the sun and moon. Not using interpolated values from the almanac. """ sun, sun_frame, moon, moon_frame = self.sun_moon_lookup.sun_moon(observation["mjd"]) observation["sunAlt"] = sun_frame.alt.rad observation["sunAz"] = sun_frame.az.rad observation["sunRA"] = sun.ra.rad observation["sunDec"] = sun.dec.rad observation["moonAlt"] = moon_frame.alt.rad observation["moonAz"] = moon_frame.az.rad observation["moonRA"] = moon.ra.rad observation["moonDec"] = moon.dec.rad observation["moonDist"] = _angular_separation( observation["RA"], observation["dec"], observation["moonRA"], observation["moonDec"], ) observation["solarElong"] = _angular_separation( observation["RA"], observation["dec"], observation["sunRA"], observation["sunDec"], ) # Moon phase from # https://github.com/egemenimre/satstuff/blob/ # master/notebooks/astropy/moon_venus_phase.ipynb sun_vec = sun.cartesian moon_vec = moon.cartesian gnd_loc = self.location.get_gcrs( Time(observation["mjd"], format="mjd") ).cartesian.without_differentials() # Generate Sun, Moon-to-location vectors sun_to_moon = sun_vec - moon_vec gnd_to_moon = gnd_loc - moon_vec sun_to_moon_unit = sun_to_moon / sun_to_moon.norm() gnd_to_moon_unit = gnd_to_moon / gnd_to_moon.norm() phase_angle_moon = np.arccos(sun_to_moon_unit.dot(gnd_to_moon_unit)) observation["moonPhase"] = phase_angle_moon / np.pi * 100 return observation
[docs] def check_mjd(self, mjd, cloud_skip=20.0): """See if an mjd is ok to observe Parameters ---------- cloud_skip : float (20) How much time to skip ahead if it's cloudy (minutes) Returns ------- mjd_ok : `bool` mdj : `float` If True, the input mjd. If false, a good mjd to skip forward to. """ passed = True new_mjd = mjd + 0 clouds = self.cloud_data(Time(mjd, format="mjd")) if clouds > self.cloud_limit: passed = False while clouds > self.cloud_limit: new_mjd = new_mjd + cloud_skip / 60.0 / 24.0 clouds = self.cloud_data(Time(new_mjd, format="mjd")) # at the end of the night, advance to the next setting twilight sun_alt = self.sun_moon_lookup.alt_at_mjd(new_mjd) if sun_alt > self.sun_rise_limit_deg: passed = False new_mjd = self.sun_moon_lookup.next_mjd_at_alt( new_mjd, altitude=self.sun_set_limit_deg, rising=False ) # Add a fudge since the new_mjd is from a fit that can be # off by machine precision new_mjd += self.delta_time_step # We're in a down time, if down, advance to the end of the downtime if not self.check_up(mjd)[0]: passed = False new_mjd = self.check_up(mjd)[1] # recursive call to make sure we skip far enough ahead if not passed: while not passed: passed, new_mjd = self.check_mjd(new_mjd) return False, new_mjd else: return True, mjd