Source code for lsst.sims.ocs.observatory.main_observatory

from builtins import object
from builtins import range
from builtins import str
import copy
import logging
import math

import palpy

from lsst.ts.dateloc import DateProfile, ObservatoryLocation
from lsst.ts.observatory.model import ObservatoryModel, Target

from lsst.sims.ocs.setup import LoggingLevel
from lsst.sims.ocs.observatory import ObsExposure, TargetExposure
from lsst.sims.ocs.observatory import SlewActivity, SlewHistory, SlewMaxSpeeds, SlewState
from lsst.sims.ocs.observatory import VariationalModel

__all__ = ["MainObservatory"]

[docs]class MainObservatory(object): """Class for the Main Observatory. This class is designed to look like the real observatory. In the default case, the observatory configuration is the main LSST obesrvatory. It uses the observatory model from the LSST Scheduler as its base information. There is an option to add variations onto the parameters and values that the model calculates to simulate real world behaviors. Attributes ---------- log : logging.Logger The logging instance. model : lsst.ts.scheduler.observatory_model.ObservatoryModel The instance of the Observatory model from the LSST Scheduler. param_dict : dict The configuration parameters for the Observatory model. """ def __init__(self, obs_site_config): """Initialize the class. Parameters ---------- obs_site_config : :class:`.ObservingSite` The instance of the observing site configuration. """ self.log = logging.getLogger("observatory.MainObservatory") observatory_location = ObservatoryLocation() observatory_location.configure({"obs_site": obs_site_config.toDict()}) self.config = None self.model = ObservatoryModel(observatory_location, LoggingLevel.WORDY.value) self.date_profile = DateProfile(0, observatory_location) self.param_dict = {} self.slew_count = 0 self.observations_made = 0 self.exposures_made = 0 self.target_exposure_list = None self.observation_exposure_list = None self.slew_history = None self.slew_final_state = None self.slew_initial_state = None self.slew_activities_list = None self.slew_activities_done = 0 self.slew_maxspeeds = None self.variational_model = None
[docs] def __getattr__(self, name): """Find attributes in lsst.ts.scheduler.observator_model.ObservatorModel as well as MainObservatory. """ try: return getattr(self.model, name) except AttributeError: cclass_name = self.__class__.__name__ aclass_name = self.model.__class__.__name__ raise AttributeError("'{}' and '{}' objects have no attribute '{}'".format(cclass_name, aclass_name, name))
[docs] def calculate_visit_time(self, target, th): """Calculate the visit time from the target and camera information. This function calculates the visit time from the current camera configuration parameters and the list of effective exposure times from the target. The visit time is calculated as: shutter_time = 2.0 * (0.5 * camera shutter time) visit_time = sum over number of exposures (shutter_time + effective exposure time) visit_time += (number of exposures - 1) * camera readout time Parameters ---------- target : SALPY_scheduler.targetC The Scheduler topic instance holding the target information. Returns ------- (float, str) The calculated visit time and a unit string (default it seconds). """ self.target_exposure_list = [] self.observation_exposure_list = [] camera_config = self.config.camera shutter_time = 2.0 * (0.5 * camera_config.shutter_time) visit_time = 0.0 for i in range(target.num_exposures): self.exposures_made += 1 effective_exposure_time = target.exposure_times[i] self.target_exposure_list.append(TargetExposure(self.exposures_made, i + 1, effective_exposure_time, target.targetId)) exposure_start_time = th.future_timestamp(visit_time, "seconds") visit_time += (shutter_time + effective_exposure_time) self.observation_exposure_list.append(ObsExposure(self.exposures_made, i + 1, effective_exposure_time, exposure_start_time, self.observations_made)) if i < (target.num_exposures - 1): visit_time += camera_config.readout_time return (visit_time, "seconds")
[docs] def configure(self, obs_config): """Configure the ObservatoryModel parameters. Parameters ---------- obs_config : :class:`.Observatory` The instance of the observatory configuration. """ self.config = obs_config self.param_dict.update(self.config.toDict()) self.model.configure(self.param_dict) self.variational_model = VariationalModel(obs_config)
[docs] def get_slew_activities(self): """Get the slew activities for the given slew. This function retrieved the list of slew activities from the model after lsst.ts.scheduler.observatory_model.ObservatoryModel::slew is called. The activites are stored in an internal structure so parameters nor returns are necessary. """ self.slew_activities_list = [] critical_activities = self.model.lastslew_criticalpath for activity, delay in self.model.lastslew_delays_dict.items(): self.slew_activities_done += 1 self.slew_activities_list.append(SlewActivity(self.slew_activities_done, activity, delay, str(activity in critical_activities), self.slew_count))
[docs] def get_slew_state(self, slew_state_info): """Get the slew state from the current state instance. This function takes a given slew state instance and copies the information to the namedtuple that will allow it to be transferred to the database. Parameters ---------- slew_state_info : lsst.ts.scheduler.observatory_model.ObservatoryState The current slew state instance. Returns ------- :class:`.SlewState` The copied slew state information. """ slew_state = SlewState(self.slew_count, slew_state_info.time, slew_state_info.ra, slew_state_info.dec, str(slew_state_info.tracking), slew_state_info.alt, slew_state_info.az, slew_state_info.pa, slew_state_info.domalt, slew_state_info.domaz, slew_state_info.telalt, slew_state_info.telaz, slew_state_info.telrot, slew_state_info.ang, slew_state_info.filter, self.slew_count) return slew_state
[docs] def observe(self, time_handler, target, observation): """Perform the observation of the given target. Parameters ---------- time_handler : :class:`.TimeHandler` An instance of the simulation's TimeHandler. target : SALPY_scheduler.targetC The Scheduler topic instance holding the target information. observation : SALPY_scheduler.observationC The Scheduler topic instance for recording the observation information. Returns ------- dict(:class:`.SlewHistory`, :class:`.SlewState`, :class:`.SlewState`, list[:class:`.SlewActivity`]) A dictionanry of all the slew information from the visit. dict(list[:class:`.TargetExposure`], list[:class:`.ObsExposure`]) A dictionary of all the exposure information from the visit. """ self.observations_made += 1 self.log.log(LoggingLevel.EXTENSIVE.value, "Starting observation {} for target {}.".format(self.observations_made, target.targetId)) slew_time = self.slew(target) time_handler.update_time(*slew_time) observation.observationId = self.observations_made observation.observation_start_time = time_handler.current_timestamp start_mjd, start_lst = self.date_profile(observation.observation_start_time) observation.observation_start_mjd = start_mjd observation.observation_start_lst = math.degrees(start_lst) observation.targetId = target.targetId observation.num_proposals = target.num_proposals for i in range(observation.num_proposals): observation.proposal_Ids[i] = target.proposal_Ids[i] observation.fieldId = target.fieldId observation.groupId = target.groupId observation.filter = target.filter observation.ra = target.ra observation.dec = target.dec observation.angle = target.angle observation.num_exposures = target.num_exposures self.log.log(LoggingLevel.EXTENSIVE.value, "Exposure Times for Target {}: {}".format(target.targetId, list(target.exposure_times))) visit_time = self.calculate_visit_time(target, time_handler) self.log.log(LoggingLevel.EXTENSIVE.value, "Visit Time for Target {}: {}".format(target.targetId, visit_time[0])) observation.visit_time = visit_time[0] for i, exposure in enumerate(self.observation_exposure_list): observation.exposure_times[i] = int(exposure.exposureTime) time_handler.update_time(*visit_time) self.log.log(LoggingLevel.EXTENSIVE.value, "Observation {} completed at {}.".format(self.observations_made, time_handler.current_timestring)) slew_info = {"slew_history": self.slew_history, "slew_initial_state": self.slew_initial_state, "slew_final_state": self.slew_final_state, "slew_activities": self.slew_activities_list, "slew_maxspeeds": self.slew_maxspeeds} exposure_info = {"target_exposures": self.target_exposure_list, "observation_exposures": self.observation_exposure_list} return slew_info, exposure_info
[docs] def slew(self, target): """Perform the slewing operation for the observatory to the given target. Parameters ---------- target : SALPY_scheduler.targetC The Scheduler topic instance holding the target information. Returns ------- float The time to slew the telescope from its current position to the target position. """ self.slew_count += 1 self.log.log(LoggingLevel.TRACE.value, "Slew count: {}".format(self.slew_count)) initial_slew_state = copy.deepcopy(self.model.current_state) self.log.log(LoggingLevel.TRACE.value, "Initial slew state: {}".format(initial_slew_state)) self.slew_initial_state = self.get_slew_state(initial_slew_state) sched_target = Target.from_topic(target) self.model.slew(sched_target) final_slew_state = copy.deepcopy(self.model.current_state) self.log.log(LoggingLevel.TRACE.value, "Final slew state: {}".format(final_slew_state)) self.slew_final_state = self.get_slew_state(final_slew_state) slew_time = (final_slew_state.time - initial_slew_state.time, "seconds") slew_distance = palpy.dsep(final_slew_state.ra_rad, final_slew_state.dec_rad, initial_slew_state.ra_rad, initial_slew_state.dec_rad) self.slew_history = SlewHistory(self.slew_count, initial_slew_state.time, final_slew_state.time, slew_time[0], math.degrees(slew_distance), self.observations_made) self.get_slew_activities() self.slew_maxspeeds = SlewMaxSpeeds(self.slew_count, final_slew_state.domalt_peakspeed, final_slew_state.domaz_peakspeed, final_slew_state.telalt_peakspeed, final_slew_state.telaz_peakspeed, final_slew_state.telrot_peakspeed, self.slew_count) return slew_time
[docs] def start_night(self, night, duration): """Perform start of night functions. Parameters ---------- night : int The current survey observing night. duration : int The survey duration in days. """ if self.variational_model.active: new_obs_config = self.variational_model.modify_parameters(night, duration) self.model.configure(new_obs_config)
[docs] def swap_filter(self, filter_to_unmount): """Perform a filter swap. This function takes a requested filter to unmount and checks it against the list of removable filters. If it is not on the list, no filter swap is performed. If it is on the list, a filter swap is performed. Parameters ---------- filter_to_unmount : str The filter requested for unmounting. """ self.log.debug("Swap out {} filter.".format(filter_to_unmount)) self.model.swap_filter(filter_to_unmount)