Example #1
0
    def __init__(self):

        self.log = logging.getLogger("schedulerDriver")

        self.params = DriverParameters()
        self.location = ObservatoryLocation()

        self.observatoryModel = ObservatoryModel(self.location, WORDY)
        self.observatoryModel2 = ObservatoryModel(self.location, WORDY)
        self.observatoryState = ObservatoryState()

        self.sky = AstronomicalSkyModel(self.location)

        self.db = FieldsDatabase()

        self.build_fields_dict()

        self.propid_counter = 0
        self.science_proposal_list = []

        self.start_time = 0.0
        self.time = 0.0
        self.targetid = 0
        self.survey_started = False
        self.isnight = False
        self.sunset_timestamp = 0.0
        self.sunrise_timestamp = 0.0
        self.survey_duration_DAYS = 0.0
        self.survey_duration_SECS = self.survey_duration_DAYS * 24 * 60 * 60.0
        self.darktime = False
        self.mounted_filter = ""
        self.unmounted_filter = ""
        self.midnight_moonphase = 0.0

        self.nulltarget = Target()
        self.nulltarget.targetid = -1
        self.nulltarget.num_exp = 1
        self.nulltarget.exp_times = [0.0]
        self.nulltarget.num_props = 1
        self.nulltarget.propid_list = [0]
        self.nulltarget.need_list = [0.0]
        self.nulltarget.bonus_list = [0.0]
        self.nulltarget.value_list = [0.0]
        self.nulltarget.propboost_list = [1.0]
        self.last_winner_target = self.nulltarget.get_copy()
        self.deep_drilling_target = None

        self.need_filter_swap = False
        self.filter_to_unmount = ""
        self.filter_to_mount = ""

        self.cloud = 0.0
        self.seeing = 0.0
Example #2
0
    def __init__(self, obs_site_config, idle_delay):
        """Initialize the class.

        Parameters
        ----------
        obs_site_config : :class:`.ObservingSite`
            The instance of the observing site configuration.
        idle_delay : float
            The delay time (seconds) to skip forward when no target is received.
        """
        self.targets_received = 0
        self.targets_missed = 0
        self.observation = None
        self.observatory_model = MainObservatory(obs_site_config)
        self.observatory_location = ObservatoryLocation(
            obs_site_config.latitude_rad, obs_site_config.longitude_rad,
            obs_site_config.height)
        self.observatory_state = None
        self.log = logging.getLogger("kernel.Sequencer")
        self.idle_delay = (idle_delay, "seconds")
        self.sky_model = AstronomicalSkyModel(self.observatory_location)
Example #3
0
    def setUp(self):
        # Need to set current time to something the sky brightness files
        # available for testing have available (MJD: 59853.-59856.).
        self.start_time = Time(59853.983, format="mjd", scale="tai")
        # Step in time when there is no target (in seconds).
        self.no_target_time_step = 120.0

        self.raw_telemetry = dict()

        self.raw_telemetry["timeHandler"] = None
        self.raw_telemetry["scheduled_targets"] = []
        self.raw_telemetry["observing_queue"] = []
        self.raw_telemetry["observatoryState"] = None
        self.raw_telemetry["bulkCloud"] = 0.0
        self.raw_telemetry["seeing"] = 1.19

        self.models = dict()

        self.models["location"] = ObservatoryLocation()
        self.models["location"].for_lsst()

        self.models["observatory_model"] = ObservatoryModel(self.models["location"])
        self.models["observatory_model"].configure_from_module()

        self.models["observatory_model"].update_state(self.start_time.unix)

        self.models["observatory_state"] = ObservatoryState()
        self.models["observatory_state"].set(
            self.models["observatory_model"].current_state
        )

        self.models["sky"] = AstronomicalSkyModel(self.models["location"])
        self.models["seeing"] = SeeingModel()
        self.models["cloud"] = CloudModel()
        self.models["downtime"] = DowntimeModel()

        self.driver = FeatureScheduler(
            models=self.models, raw_telemetry=self.raw_telemetry
        )

        self.config = types.SimpleNamespace(
            driver_configuration=dict(
                scheduler_config="",
                force=True,
                default_observing_script_name="standard_visit.py",
                default_observing_script_is_standard=True,
                stop_tracking_observing_script_name="stop_tracking.py",
                stop_tracking_observing_script_is_standard=True,
            )
        )

        self.files_to_delete = []
Example #4
0
    def setUp(self):

        self.raw_telemetry = dict()

        self.raw_telemetry["timeHandler"] = None
        self.raw_telemetry["scheduled_targets"] = []
        self.raw_telemetry["observing_queue"] = []
        self.raw_telemetry["observatoryState"] = None
        self.raw_telemetry["bulkCloud"] = 0.0
        self.raw_telemetry["seeing"] = 1.0

        self.models = dict()

        self.models["location"] = ObservatoryLocation()
        self.models["location"].for_lsst()

        self.models["observatory_model"] = ObservatoryModel(self.models["location"])
        self.models["observatory_model"].configure_from_module()

        self.models["observatory_model"].update_state(current_tai())

        self.models["observatory_state"] = ObservatoryState()
        self.models["observatory_state"].set(
            self.models["observatory_model"].current_state
        )

        self.models["sky"] = AstronomicalSkyModel(self.models["location"])
        self.models["seeing"] = SeeingModel()
        self.models["cloud"] = CloudModel()
        self.models["downtime"] = DowntimeModel()

        self.driver = SequentialScheduler(
            models=self.models,
            raw_telemetry=self.raw_telemetry,
        )

        self.config = types.SimpleNamespace(
            driver_configuration=dict(
                observing_list=pathlib.Path(__file__)
                .parents[1]
                .joinpath("tests", "data", "test_observing_list.yaml"),
                general_propos=["Test"],
                default_observing_script_name="standard_visit.py",
                default_observing_script_is_standard=True,
                stop_tracking_observing_script_name="stop_tracking.py",
                stop_tracking_observing_script_is_standard=True,
            )
        )

        self.files_to_delete = []
Example #5
0
    def __init__(self):
        self.log = logging.getLogger("schedulerDriver")

        self.params = DriverParameters()
        self.location = ObservatoryLocation()

        self.observatoryModel = ObservatoryModel(self.location, WORDY)
        self.observatoryModel2 = ObservatoryModel(self.location, WORDY)
        self.observatoryState = ObservatoryState()

        self.sky = AstronomicalSkyModel(self.location)

        self.propid_counter = 0
        self.night = 0
        self.start_time = 0.0
        self.time = 0.0
        self.targetid = 0
        self.survey_started = False
        self.isnight = False
        self.sunset_timestamp = 0.0
        self.sunrise_timestamp = 0.0
        self.survey_duration_DAYS = 0.0
        self.survey_duration_SECS = self.survey_duration_DAYS * 24 * 60 * 60.0
        self.darktime = False
        self.mounted_filter = ""
        self.unmounted_filter = ""
        self.midnight_moonphase = 0.0

        self.nulltarget = Target()
        self.nulltarget.targetid = -1
        self.nulltarget.num_exp = 1
        self.nulltarget.exp_times = [0.0]
        self.nulltarget.num_props = 1
        self.nulltarget.propid_list = [0]
        self.nulltarget.need_list = [0.0]
        self.nulltarget.bonus_list = [0.0]
        self.nulltarget.value_list = [0.0]
        self.nulltarget.propboost_list = [1.0]

        self.last_winner_target = self.nulltarget.get_copy()
        self.deep_drilling_target = None

        self.need_filter_swap = False
        self.filter_to_unmount = ""
        self.filter_to_mount = ""

        self.cloud = 0.0
        self.seeing = 0.0
Example #6
0
class Sequencer(object):
    """Handle the observation of a target.

    This class is responsible for taking a target from the Scheduler and performing the necessary steps to
    make an astronomical observation. It is then responsible for handing that observation back.

    Attributes
    ----------
    targets_received : int
        Counter for the number of targets received by the sequencer.
    targets_missed : int
        Counter for the number of targets that were actually observed due to scheduler rejection.
    observations_made : int
        Counter for the number of observations made by the sequencer.
    observation : SALPY_scheduler.observationC
        DDS topic instance for the observation information.
    observatory_model : :class:`.MainObservatory`
        Instance of the SOCS observatory model.
    observatory_state : SALPY_scheduler.observatoryStateC
        DDS topic instance for the observatory state information.
    idle_delay : float
        Time (units=seconds) to wait when a missed target is received.
    log : logging.Logger
        The logging instance.
    """
    def __init__(self, obs_site_config, idle_delay):
        """Initialize the class.

        Parameters
        ----------
        obs_site_config : :class:`.ObservingSite`
            The instance of the observing site configuration.
        idle_delay : float
            The delay time (seconds) to skip forward when no target is received.
        """
        self.targets_received = 0
        self.targets_missed = 0
        self.observation = None
        self.observatory_model = MainObservatory(obs_site_config)
        self.observatory_location = ObservatoryLocation(
            obs_site_config.latitude_rad, obs_site_config.longitude_rad,
            obs_site_config.height)
        self.observatory_state = None
        self.log = logging.getLogger("kernel.Sequencer")
        self.idle_delay = (idle_delay, "seconds")
        self.sky_model = AstronomicalSkyModel(self.observatory_location)

    @property
    def observations_made(self):
        """Get the number of observations made.

        Returns
        -------
        int
        """
        return self.observatory_model.observations_made

    def end_night(self):
        """Perform end of night functions.
        """
        # Park the telescope for the day.
        self.observatory_model.park()

    def get_observatory_state(self, timestamp):
        """Return the observatory state in a DDS topic instance.

        Parameters
        ----------
        timestamp : float
            The current timestamp at the state retrieval request.

        Return
        ------
        SALPY_scheduler.observatoryStateC
        """
        self.observatory_model.update_state(timestamp)
        obs_current_state = self.observatory_model.current_state

        self.observatory_state.timestamp = timestamp
        self.observatory_state.pointing_ra = obs_current_state.ra
        self.observatory_state.pointing_dec = obs_current_state.dec
        self.observatory_state.pointing_angle = obs_current_state.ang
        self.observatory_state.pointing_altitude = obs_current_state.alt
        self.observatory_state.pointing_azimuth = obs_current_state.az
        self.observatory_state.pointing_pa = obs_current_state.pa
        self.observatory_state.pointing_rot = obs_current_state.rot
        self.observatory_state.tracking = obs_current_state.tracking
        self.observatory_state.telescope_altitude = obs_current_state.telalt
        self.observatory_state.telescope_azimuth = obs_current_state.telaz
        self.observatory_state.telescope_rotator = obs_current_state.telrot
        self.observatory_state.dome_altitude = obs_current_state.domalt
        self.observatory_state.dome_azimuth = obs_current_state.domaz
        self.observatory_state.filter_position = obs_current_state.filter
        self.observatory_state.filter_mounted = ",".join(
            obs_current_state.mountedfilters)
        self.observatory_state.filter_unmounted = ','.join(
            obs_current_state.unmountedfilters)

        return self.observatory_state

    def initialize(self, sal, obs_config):
        """Perform initialization steps.

        This function handles gathering the observation telemetry topic from the given SalManager instance.

        Parameters
        ----------
        sal : :class:`.SalManager`
            A SalManager instance.
        obs_config : :class:`.Observatory`
            The instance of the observatory configuration.
        """
        self.observation = sal.set_publish_topic("observation")
        self.observatory_state = sal.set_publish_topic("observatoryState")
        self.observatory_model.configure(obs_config)

    def finalize(self):
        """Perform finalization steps.

        This function logs the number or targets received and observations made.
        """
        self.log.info("Number of targets received: {}".format(
            self.targets_received))
        self.log.info("Number of observations made: {}".format(
            self.observations_made))
        self.log.info("Number of targets missed: {}".format(
            self.targets_missed))

    def observe_target(self, target, th):
        """Observe the given target.

        This function performs the necessary steps to observe the given target. The current steps are:

          * Update the simulation time after "slewing"
          * Copy target information to observation
          * Update the simulation time after "visit"

        If the targetId is -1, this means a target was not offered by the Scheduler. Time is forwarded
        by the idle delay time and slew and exposure information are set to None. The observation takes the
        target's Id.

        Parameters
        ----------
        target : SALPY_scheduler.targetC
            A target telemetry topic containing the current target information.
        th : :class:`.TimeHandler`
            An instance of the simulation's TimeHandler.

        Returns
        -------
        SALPY_scheduler.observationC
            An observation telemetry topic containing the observed target parameters.
        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.
        """
        if target.targetId != -1:
            self.log.log(LoggingLevel.EXTENSIVE.value,
                         "Received target {}".format(target.targetId))
            self.targets_received += 1

            self.sky_model.update(target.request_time)
            target.request_mjd = self.sky_model.date_profile.mjd

            slew_info, exposure_info = self.observatory_model.observe(
                th, target, self.observation)
            self.sky_model.update(self.observation.observation_start_time)

            nid = numpy.array([target.fieldId])
            nra = numpy.radians(numpy.array([self.observation.ra]))
            ndec = numpy.radians(numpy.array([self.observation.dec]))

            sky_mags = self.sky_model.get_sky_brightness(
                nid, extrapolate=True, override_exclude_planets=False)
            attrs = self.sky_model.get_target_information(nid, nra, ndec)
            msi = self.sky_model.get_moon_sun_info(nra, ndec)

            self.observation.sky_brightness = sky_mags[
                self.observation.filter][0]
            self.observation.airmass = attrs["airmass"][0]
            self.observation.altitude = numpy.degrees(attrs["altitude"][0])
            self.observation.azimuth = numpy.degrees(attrs["azimuth"][0])
            self.observation.moon_ra = numpy.degrees(msi["moonRA"])
            self.observation.moon_dec = numpy.degrees(msi["moonDec"])
            self.observation.moon_alt = numpy.degrees(msi["moonAlt"][0])
            self.observation.moon_az = numpy.degrees(msi["moonAz"][0])
            self.observation.moon_phase = msi["moonPhase"]
            self.observation.moon_distance = numpy.degrees(msi["moonDist"][0])
            self.observation.sun_alt = numpy.degrees(msi["sunAlt"][0])
            self.observation.sun_az = numpy.degrees(msi["sunAz"][0])
            self.observation.sun_ra = numpy.degrees(msi["sunRA"])
            self.observation.sun_dec = numpy.degrees(msi["sunDec"])
            self.observation.solar_elong = numpy.degrees(msi["solarElong"][0])
        else:
            self.log.log(LoggingLevel.EXTENSIVE.value, "No target received!")
            self.observation.observationId = target.targetId
            self.observation.targetId = target.targetId
            if target.filter == '':
                self.observation.filter = 'z'
            if target.seeing == 0.0:
                self.observation.seeing_fwhm_eff = 0.1
            if sum(target.exposure_times) == 0.0:
                for i in range(target.num_exposures):
                    self.observation.exposure_times[i] = 15
                self.observation.num_exposures = 1
            if target.airmass == 0.0:
                self.observation.airmass = 1.0
            if target.sky_brightness == 0.0:
                self.observation.sky_brightness = 30.0
            slew_info = None
            exposure_info = None
            th.update_time(*self.idle_delay)
            self.targets_missed += 1

        return self.observation, slew_info, exposure_info

    def sky_brightness_config(self):
        """Get the configuration from the SkyModelPre files.

        Returns
        -------
        list[tuple(key, value)]
        """
        return self.sky_model.sky_brightness_config()

    def start_day(self, filter_swap):
        """Perform start of day functions.

        Parameters
        ----------
        filter_swap : scheduler_filterSwapC
            The instance of the filter swap information.
        """
        if filter_swap.need_swap:
            self.observatory_model.swap_filter(filter_swap.filter_to_unmount)

    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.
        """
        self.observatory_model.start_night(night, duration)
Example #7
0
class Driver(object):
    def __init__(self):

        self.log = logging.getLogger("schedulerDriver")

        self.params = DriverParameters()
        self.location = ObservatoryLocation()

        self.observatoryModel = ObservatoryModel(self.location, WORDY)
        self.observatoryModel2 = ObservatoryModel(self.location, WORDY)
        self.observatoryState = ObservatoryState()

        self.sky = AstronomicalSkyModel(self.location)

        self.db = FieldsDatabase()

        self.build_fields_dict()

        self.propid_counter = 0
        self.science_proposal_list = []

        self.start_time = 0.0
        self.time = 0.0
        self.targetid = 0
        self.survey_started = False
        self.isnight = False
        self.sunset_timestamp = 0.0
        self.sunrise_timestamp = 0.0
        self.survey_duration_DAYS = 0.0
        self.survey_duration_SECS = self.survey_duration_DAYS * 24 * 60 * 60.0
        self.darktime = False
        self.mounted_filter = ""
        self.unmounted_filter = ""
        self.midnight_moonphase = 0.0

        self.nulltarget = Target()
        self.nulltarget.targetid = -1
        self.nulltarget.num_exp = 1
        self.nulltarget.exp_times = [0.0]
        self.nulltarget.num_props = 1
        self.nulltarget.propid_list = [0]
        self.nulltarget.need_list = [0.0]
        self.nulltarget.bonus_list = [0.0]
        self.nulltarget.value_list = [0.0]
        self.nulltarget.propboost_list = [1.0]
        self.last_winner_target = self.nulltarget.get_copy()
        self.deep_drilling_target = None

        self.need_filter_swap = False
        self.filter_to_unmount = ""
        self.filter_to_mount = ""

        self.cloud = 0.0
        self.seeing = 0.0

    def configure_survey(self, survey_conf_file):

        prop_conf_path = os.path.dirname(survey_conf_file)
        confdict = read_conf_file(survey_conf_file)

        self.survey_duration_DAYS = confdict["survey"]["survey_duration"]
        self.survey_duration_SECS = self.survey_duration_DAYS * 24 * 60 * 60.0

        self.propid_counter = 0
        self.science_proposal_list = []

        if 'scripted_propconf' in confdict["proposals"]:
            scripted_propconflist = confdict["proposals"]["scripted_propconf"]
        else:
            scripted_propconflist = []
        if not isinstance(scripted_propconflist, list):
            # turn it into a list with one entry
            propconf = scripted_propconflist
            scripted_propconflist = []
            scripted_propconflist.append(propconf)
        self.log.info("configure_survey: scripted proposals %s" % (scripted_propconflist))
        for k in range(len(scripted_propconflist)):
            self.propid_counter += 1
            scripted_prop = ScriptedProposal(self.propid_counter,
                                             os.path.join(prop_conf_path,
                                                          "{}".format(scripted_propconflist[k])),
                                             self.sky)
            self.science_proposal_list.append(scripted_prop)

        if 'areadistribution_propconf' in confdict["proposals"]:
            areadistribution_propconflist = confdict["proposals"]["areadistribution_propconf"]
        else:
            areadistribution_propconflist = []
            self.log.info("areadistributionPropConf:%s default" % (areadistribution_propconflist))
        if not isinstance(areadistribution_propconflist, list):
            # turn it into a list with one entry
            propconf = areadistribution_propconflist
            areadistribution_propconflist = []
            areadistribution_propconflist.append(propconf)
        self.log.info("init: areadistribution proposals %s" % (areadistribution_propconflist))
        for k in range(len(areadistribution_propconflist)):
            self.propid_counter += 1
            configfilepath = os.path.join(prop_conf_path, "{}".format(areadistribution_propconflist[k]))
            (path, name_ext) = os.path.split(configfilepath)
            (name, ext) = os.path.splitext(name_ext)
            proposal_confdict = read_conf_file(configfilepath)
            self.create_area_proposal(self.propid_counter, name, proposal_confdict)

        for prop in self.science_proposal_list:
            prop.configure_constraints(self.params)

    def configure_duration(self, survey_duration):

        self.survey_duration_DAYS = survey_duration
        self.survey_duration_SECS = survey_duration * 24 * 60 * 60.0

    def configure(self, confdict):

        self.params.configure(confdict)
        self.log.log(WORDY,
                     "configure: coadd_values=%s" % (self.params.coadd_values))
        self.log.log(WORDY,
                     "configure: time_balancing=%s" % (self.params.time_balancing))
        self.log.log(WORDY,
                     "configure: timecost_dc=%.3f" % (self.params.timecost_dc))
        self.log.log(WORDY,
                     "configure: timecost_dt=%.3f" % (self.params.timecost_dt))
        self.log.log(WORDY,
                     "configure: timecost_k=%.3f" % (self.params.timecost_k))
        self.log.log(WORDY,
                     "configure: timecost_weight=%.3f" % (self.params.timecost_weight))
        self.log.log(WORDY,
                     "configure: night_boundary=%.1f" % (self.params.night_boundary))
        self.log.log(WORDY,
                     "configure: ignore_sky_brightness=%s" % (self.params.ignore_sky_brightness))
        self.log.log(WORDY,
                     "configure: ignore_airmass=%s" % (self.params.ignore_airmass))
        self.log.log(WORDY,
                     "configure: ignore_clouds=%s" % (self.params.ignore_clouds))
        self.log.log(WORDY,
                     "configure: ignore_seeing=%s" % (self.params.ignore_seeing))
        self.log.log(WORDY,
                     "configure: new_moon_phase_threshold=%.2f" % (self.params.new_moon_phase_threshold))

        for prop in self.science_proposal_list:
            prop.configure_constraints(self.params)

    def configure_location(self, confdict):

        self.location.configure(confdict)
        self.observatoryModel.location.configure(confdict)
        self.observatoryModel2.location.configure(confdict)
        self.sky.update_location(self.location)

    def configure_observatory(self, confdict):

        self.observatoryModel.configure(confdict)
        self.observatoryModel2.configure(confdict)

    def configure_telescope(self, confdict):

        self.observatoryModel.configure_telescope(confdict)
        self.observatoryModel2.configure_telescope(confdict)

    def configure_rotator(self, confdict):

        self.observatoryModel.configure_rotator(confdict)
        self.observatoryModel2.configure_rotator(confdict)

    def configure_dome(self, confdict):

        self.observatoryModel.configure_dome(confdict)
        self.observatoryModel2.configure_dome(confdict)

    def configure_optics(self, confdict):

        self.observatoryModel.configure_optics(confdict)
        self.observatoryModel2.configure_optics(confdict)

    def configure_camera(self, confdict):

        self.observatoryModel.configure_camera(confdict)
        self.observatoryModel2.configure_camera(confdict)

    def configure_slew(self, confdict):

        self.observatoryModel.configure_slew(confdict)
        self.observatoryModel2.configure_slew(confdict)

    def configure_park(self, confdict):

        self.observatoryModel.configure_park(confdict)
        self.observatoryModel2.configure_park(confdict)

    def create_area_proposal(self, propid, name, config_dict):

        self.propid_counter += 1
        area_prop = AreaDistributionProposal(propid, name, config_dict, self.sky)
        area_prop.configure_constraints(self.params)
        self.science_proposal_list.append(area_prop)

    def create_sequence_proposal(self, propid, name, config_dict):

        self.propid_counter += 1
        seq_prop = TimeDistributionProposal(propid, name, config_dict, self.sky)
        seq_prop.configure_constraints(self.params)
        self.science_proposal_list.append(seq_prop)

    def build_fields_dict(self):

        sql = "select * from Field"
        res = self.db.query(sql)

        self.fields_dict = {}
        for row in res:
            field = Field()
            fieldid = row[0]
            field.fieldid = fieldid
            field.fov_rad = math.radians(row[1])
            field.ra_rad = math.radians(row[2])
            field.dec_rad = math.radians(row[3])
            field.gl_rad = math.radians(row[4])
            field.gb_rad = math.radians(row[5])
            field.el_rad = math.radians(row[6])
            field.eb_rad = math.radians(row[7])
            self.fields_dict[fieldid] = field
            self.log.log(EXTENSIVE, "buildFieldsTable: %s" % (self.fields_dict[fieldid]))
        self.log.info("buildFieldsTable: %d fields" % (len(self.fields_dict)))

    def get_fields_dict(self):

        return self.fields_dict

    def start_survey(self, timestamp, night):

        self.start_time = timestamp
        self.log.info("start_survey t=%.6f" % timestamp)

        self.survey_started = True
        for prop in self.science_proposal_list:
            prop.start_survey()

        self.sky.update(timestamp)
        (sunset, sunrise) = self.sky.get_night_boundaries(self.params.night_boundary)
        self.log.debug("start_survey sunset=%.6f sunrise=%.6f" % (sunset, sunrise))
        # if round(sunset) <= round(timestamp) < round(sunrise):
        if sunset <= timestamp < sunrise:
            self.start_night(timestamp, night)

        self.sunset_timestamp = sunset
        self.sunrise_timestamp = sunrise

    def end_survey(self):

        self.log.info("end_survey")

        for prop in self.science_proposal_list:
            prop.end_survey()

    def start_night(self, timestamp, night):

        timeprogress = (timestamp - self.start_time) / self.survey_duration_SECS
        self.log.info("start_night t=%.6f, night=%d timeprogress=%.2f%%" %
                      (timestamp, night, 100 * timeprogress))

        self.isnight = True

        for prop in self.science_proposal_list:
            prop.start_night(timestamp, self.observatoryModel.current_state.mountedfilters, night)

    def end_night(self, timestamp, night):

        timeprogress = (timestamp - self.start_time) / self.survey_duration_SECS
        self.log.info("end_night t=%.6f, night=%d timeprogress=%.2f%%" %
                      (timestamp, night, 100 * timeprogress))

        self.isnight = False

        self.last_winner_target = self.nulltarget
        self.deep_drilling_target = None

        total_filter_visits_dict = {}
        total_filter_goal_dict = {}
        total_filter_progress_dict = {}
        for prop in self.science_proposal_list:
            prop.end_night(timestamp)
            filter_visits_dict = {}
            filter_goal_dict = {}
            filter_progress_dict = {}
            for filter in self.observatoryModel.filters:
                if filter not in total_filter_visits_dict:
                    total_filter_visits_dict[filter] = 0
                    total_filter_goal_dict[filter] = 0
                filter_visits_dict[filter] = prop.get_filter_visits(filter)
                filter_goal_dict[filter] = prop.get_filter_goal(filter)
                filter_progress_dict[filter] = prop.get_filter_progress(filter)
                total_filter_visits_dict[filter] += filter_visits_dict[filter]
                total_filter_goal_dict[filter] += filter_goal_dict[filter]
                self.log.debug("end_night propid=%d name=%s filter=%s progress=%.2f%%" %
                               (prop.propid, prop.name, filter, 100 * filter_progress_dict[filter]))
        for filter in self.observatoryModel.filters:
            if total_filter_goal_dict[filter] > 0:
                total_filter_progress_dict[filter] = \
                    float(total_filter_visits_dict[filter]) / total_filter_goal_dict[filter]
            else:
                total_filter_progress_dict[filter] = 0.0
            self.log.info("end_night filter=%s progress=%.2f%%" %
                          (filter, 100 * total_filter_progress_dict[filter]))

        previous_midnight_moonphase = self.midnight_moonphase
        self.sky.update(timestamp)
        (sunset, sunrise) = self.sky.get_night_boundaries(self.params.night_boundary)
        self.log.debug("end_night sunset=%.6f sunrise=%.6f" % (sunset, sunrise))

        self.sunset_timestamp = sunset
        self.sunrise_timestamp = sunrise
        next_midnight = (sunset + sunrise) / 2
        self.sky.update(next_midnight)
        info = self.sky.get_moon_sun_info(numpy.array([0.0]), numpy.array([0.0]))
        self.midnight_moonphase = info["moonPhase"]
        self.log.info("end_night next moonphase=%.2f%%" % (self.midnight_moonphase))

        self.need_filter_swap = False
        self.filter_to_mount = ""
        self.filter_to_unmount = ""
        if self.darktime:
            if self.midnight_moonphase > previous_midnight_moonphase:
                self.log.info("end_night dark time waxing")
                if self.midnight_moonphase > self.params.new_moon_phase_threshold:
                    self.need_filter_swap = True
                    self.filter_to_mount = self.unmounted_filter
                    self.filter_to_unmount = self.mounted_filter
                    self.darktime = False
            else:
                self.log.info("end_night dark time waning")
        else:
            if self.midnight_moonphase < previous_midnight_moonphase:
                self.log.info("end_night bright time waning")
                if self.midnight_moonphase < self.params.new_moon_phase_threshold:
                    self.need_filter_swap = True
                    self.filter_to_mount = self.observatoryModel.params.filter_darktime
                    max_progress = -1.0
                    for filter in self.observatoryModel.params.filter_removable_list:
                        if total_filter_progress_dict[filter] > max_progress:
                            self.filter_to_unmount = filter
                            max_progress = total_filter_progress_dict[filter]
                    self.darktime = True
            else:
                self.log.info("end_night bright time waxing")

        if self.need_filter_swap:
            self.log.debug("end_night filter swap %s=>cam=>%s" %
                           (self.filter_to_mount, self.filter_to_unmount))

    def swap_filter(self, filter_to_unmount, filter_to_mount):

        self.log.info("swap_filter swap %s=>cam=>%s" % (filter_to_mount, filter_to_unmount))

        self.observatoryModel.swap_filter(filter_to_unmount)

        self.unmounted_filter = filter_to_unmount
        self.mounted_filter = filter_to_mount

        return

    def update_time(self, timestamp, night):

        self.time = timestamp
        self.observatoryModel.update_state(self.time)
        if not self.survey_started:
            self.start_survey(timestamp, night)

        if self.isnight:
            # if round(timestamp) >= round(self.sunrise_timestamp):
            if timestamp >= self.sunrise_timestamp:
                self.end_night(timestamp, night)
        else:
            # if round(timestamp) >= round(self.sunset_timestamp):
            if timestamp >= self.sunset_timestamp:
                self.start_night(timestamp, night)

        return self.isnight

    def get_need_filter_swap(self):

        return (self.need_filter_swap, self.filter_to_unmount, self.filter_to_mount)

    def update_internal_conditions(self, observatory_state, night):

        if observatory_state.unmountedfilters != self.observatoryModel.current_state.unmountedfilters:
            unmount = observatory_state.unmountedfilters[0]
            mount = self.observatoryModel.current_state.unmountedfilters[0]
            self.swap_filter(unmount, mount)
            for prop in self.science_proposal_list:
                prop.start_night(observatory_state.time, observatory_state.mountedfilters, night)

        self.time = observatory_state.time
        self.observatoryModel.set_state(observatory_state)
        self.observatoryState.set(observatory_state)

    def update_external_conditions(self, cloud, seeing):

        self.cloud = cloud
        self.seeing = seeing

        return

    def select_next_target(self):

        if not self.isnight:
            return self.nulltarget

        targets_dict = {}
        ranked_targets_list = []
        propboost_dict = {}
        sumboost = 0.0

        timeprogress = (self.time - self.start_time) / self.survey_duration_SECS
        for prop in self.science_proposal_list:

            progress = prop.get_progress()
            if self.params.time_balancing:
                if progress > 0.0:
                    if timeprogress < 1.0:
                        needindex = (1.0 - progress) / (1.0 - timeprogress)
                    else:
                        needindex = 0.0
                    if timeprogress > 0.0:
                        progressindex = progress / timeprogress
                    else:
                        progressindex = 1.0
                    propboost_dict[prop.propid] = needindex / progressindex
                else:
                    propboost_dict[prop.propid] = 1.0
            else:
                propboost_dict[prop.propid] = 1.0
            sumboost += propboost_dict[prop.propid]

        if self.deep_drilling_target is not None:
            self.log.debug("select_next_target: in deep drilling %s" % str(self.deep_drilling_target))

        if self.observatoryModel.is_filter_change_allowed():
            constrained_filter = None
        else:
            constrained_filter = self.observatoryModel.current_state.filter
        num_filter_changes = self.observatoryModel.get_number_filter_changes()
        delta_burst = self.observatoryModel.get_delta_filter_burst()
        delta_avg = self.observatoryModel.get_delta_filter_avg()
        self.log.debug("select_next_target: filter changes num=%i tburst=%.1f tavg=%.1f constrained=%s" %
                       (num_filter_changes, delta_burst, delta_avg, constrained_filter))

        for prop in self.science_proposal_list:
            propboost_dict[prop.propid] = \
                (propboost_dict[prop.propid] * len(self.science_proposal_list) / sumboost) ** self.params.propboost_weight

            proptarget_list = prop.suggest_targets(self.time,
                                                   self.deep_drilling_target, constrained_filter,
                                                   self.cloud, self.seeing)
            self.log.log(EXTENSIVE, "select_next_target propid=%d name=%s "
                         "targets=%d progress=%.2f%% propboost=%.3f" %
                         (prop.propid, prop.name,
                          len(proptarget_list), 100 * progress, propboost_dict[prop.propid]))

            for target in proptarget_list:
                target.num_props = 1
                target.propboost = propboost_dict[prop.propid]
                target.propid_list = [prop.propid]
                target.need_list = [target.need]
                target.bonus_list = [target.bonus]
                target.value_list = [target.value]
                target.propboost_list = [target.propboost]
                target.sequenceid_list = [target.sequenceid]
                target.subsequencename_list = [target.subsequencename]
                target.groupid_list = [target.groupid]
                target.groupix_list = [target.groupix]
                target.is_deep_drilling_list = [target.is_deep_drilling]
                target.is_dd_firstvisit_list = [target.is_dd_firstvisit]
                target.remaining_dd_visits_list = [target.remaining_dd_visits]
                target.dd_exposures_list = [target.dd_exposures]
                target.dd_filterchanges_list = [target.dd_filterchanges]
                target.dd_exptime_list = [target.dd_exptime]

                fieldfilter = (target.fieldid, target.filter)
                if fieldfilter in targets_dict:
                    if self.params.coadd_values:
                        targets_dict[fieldfilter][0] = targets_dict[fieldfilter][0].get_copy()
                        targets_dict[fieldfilter][0].need += target.need
                        targets_dict[fieldfilter][0].bonus += target.bonus
                        targets_dict[fieldfilter][0].value += target.value
                        targets_dict[fieldfilter][0].propboost += target.propboost
                        if target.is_deep_drilling:
                            # overrides to make the coadded target a consistent deep drilling
                            targets_dict[fieldfilter][0].is_deep_drilling = target.is_deep_drilling
                            targets_dict[fieldfilter][0].is_dd_firstvisit = target.is_dd_firstvisit
                            targets_dict[fieldfilter][0].remaining_dd_visits = target.remaining_dd_visits
                            targets_dict[fieldfilter][0].dd_exposures = target.dd_exposures
                            targets_dict[fieldfilter][0].dd_filterchanges = target.dd_filterchanges
                            targets_dict[fieldfilter][0].dd_exptime = target.dd_exptime
                            targets_dict[fieldfilter][0].sequenceid = target.sequenceid
                            targets_dict[fieldfilter][0].subsequencename = target.subsequencename
                            targets_dict[fieldfilter][0].groupid = target.groupid
                            targets_dict[fieldfilter][0].groupix = target.groupix
                        else:
                            # new target to coadd is not deep drilling
                            if not targets_dict[fieldfilter][0].is_deep_drilling:
                                # coadded target is not deep drilling
                                # overrides with new sequence information
                                targets_dict[fieldfilter][0].sequenceid = target.sequenceid
                                targets_dict[fieldfilter][0].subsequencename = target.subsequencename
                                targets_dict[fieldfilter][0].groupid = target.groupid
                                targets_dict[fieldfilter][0].groupix = target.groupix
                            # if coadded target is already deep drilling, don't override
                        targets_dict[fieldfilter][0].num_props += 1
                        targets_dict[fieldfilter][0].propid_list.append(prop.propid)
                        targets_dict[fieldfilter][0].need_list.append(target.need)
                        targets_dict[fieldfilter][0].bonus_list.append(target.bonus)
                        targets_dict[fieldfilter][0].value_list.append(target.value)
                        targets_dict[fieldfilter][0].propboost_list.append(target.propboost)
                        targets_dict[fieldfilter][0].sequenceid_list.append(target.sequenceid)
                        targets_dict[fieldfilter][0].subsequencename_list.append(target.subsequencename)
                        targets_dict[fieldfilter][0].groupid_list.append(target.groupid)
                        targets_dict[fieldfilter][0].groupix_list.append(target.groupix)
                        targets_dict[fieldfilter][0].is_deep_drilling_list.append(target.is_deep_drilling)
                        targets_dict[fieldfilter][0].is_dd_firstvisit_list.append(target.is_dd_firstvisit)
                        targets_dict[fieldfilter][0].remaining_dd_visits_list.append(
                            target.remaining_dd_visits)
                        targets_dict[fieldfilter][0].dd_exposures_list.append(target.dd_exposures)
                        targets_dict[fieldfilter][0].dd_filterchanges_list.append(target.dd_filterchanges)
                        targets_dict[fieldfilter][0].dd_exptime_list.append(target.dd_exptime)
                    else:
                        targets_dict[fieldfilter].append(target)
                else:
                    targets_dict[fieldfilter] = [target]

        filtercost = self.compute_filterchange_cost() * self.params.filtercost_weight
        for fieldfilter in targets_dict:
            slewtime = self.observatoryModel.get_slew_delay(targets_dict[fieldfilter][0])
            if slewtime >= 0:
                timecost = self.compute_slewtime_cost(slewtime) * self.params.timecost_weight
                for target in targets_dict[fieldfilter]:
                    target.slewtime = slewtime
                    if target.filter != self.observatoryModel.current_state.filter:
                        target.cost = timecost + filtercost
                    else:
                        target.cost = timecost
                    target.rank = (target.value * target.propboost) - target.cost
                    ranked_targets_list.append((-target.rank, target))

        sorted_list = sorted(ranked_targets_list, key=itemgetter(0))

        winner_found = False
        while len(sorted_list) > 0 and not winner_found:
            winner_target = sorted_list.pop(0)[1]

            self.observatoryModel2.set_state(self.observatoryState)
            self.observatoryModel2.observe(winner_target)
            if winner_target.is_dd_firstvisit:
                ttime = self.observatoryModel2.get_deep_drilling_time(winner_target)
            else:
                ttime = 0.0
            ntime = self.observatoryModel2.current_state.time + ttime + 30.0
            if ntime < self.sunrise_timestamp:
                self.observatoryModel2.update_state(ntime)
                if self.observatoryModel2.current_state.tracking:
                    self.targetid += 1
                    winner_target.targetid = self.targetid
                    winner_target.time = self.time
                    winner_found = True
                else:
                    self.log.debug("select_next_target: target rejected ttime=%.1f %s" %
                                   (ttime, str(winner_target)))
                    self.log.debug("select_next_target: state rejected %s" %
                                   str(self.observatoryModel2.current_state))
            else:
                self.log.debug("select_next_target: target rejected ttime=%.1f %s" %
                               (ttime, str(winner_target)))
                self.log.debug("select_next_target: rejected due to end of night")
                if ttime == 0.0:
                    # ttime == 0 means it is a regular visit (not DD)
                    # if there is no time left for a single visit then quit
                    break

        if winner_found:
            if not self.params.coadd_values:
                first_target = targets_dict[(winner_target.fieldid, winner_target.filter)][0]
                if first_target.propid != winner_target.propid:
                    winner_target.copy_driver_state(first_target)
            self.last_winner_target = winner_target.get_copy()
        else:
            self.last_winner_target = self.nulltarget

        return self.last_winner_target

    def register_observation(self, observation):

        target_list = []
        if observation.targetid > 0:
            if self.observation_fulfills_target(observation, self.last_winner_target):
                observation = self.last_winner_target
            else:
                self.log.info("register_observation: unexpected observation %s" % str(observation))

            for prop in self.science_proposal_list:
                target = prop.register_observation(observation)
                if target is not None:
                    target_list.append(target)

        self.last_observation = observation.get_copy()
        if self.last_observation.is_deep_drilling and (self.last_observation.remaining_dd_visits > 1):
            self.deep_drilling_target = self.last_observation
        else:
            self.deep_drilling_target = None

        return target_list

    def compute_slewtime_cost(self, slewtime):

        cost = (self.params.timecost_k / (slewtime + self.params.timecost_dt) -
                self.params.timecost_dc - self.params.timecost_cref) / (1.0 - self.params.timecost_cref)
        #cost = self.params.timecost_k / (slewtime + self.params.timecost_dt) - self.params.timecost_dc

        return cost

    def compute_filterchange_cost(self):

        t = self.observatoryModel.get_delta_last_filterchange()
        T = self.observatoryModel.params.filter_max_changes_avg_interval
        if t < T:
            cost = 1.0 - t / T
        else:
            cost = 0.0

        return cost

    def observation_fulfills_target(self, observ, target):

        return (observ.fieldid == target.fieldid) and (observ.filter == target.filter)
Example #8
0
class Driver(object):
    def __init__(self):
        self.log = logging.getLogger("schedulerDriver")

        self.params = DriverParameters()
        self.location = ObservatoryLocation()

        self.observatoryModel = ObservatoryModel(self.location, WORDY)
        self.observatoryModel2 = ObservatoryModel(self.location, WORDY)
        self.observatoryState = ObservatoryState()

        self.sky = AstronomicalSkyModel(self.location)

        self.propid_counter = 0
        self.night = 0
        self.start_time = 0.0
        self.time = 0.0
        self.targetid = 0
        self.survey_started = False
        self.isnight = False
        self.sunset_timestamp = 0.0
        self.sunrise_timestamp = 0.0
        self.survey_duration_DAYS = 0.0
        self.survey_duration_SECS = self.survey_duration_DAYS * 24 * 60 * 60.0
        self.darktime = False
        self.mounted_filter = ""
        self.unmounted_filter = ""
        self.midnight_moonphase = 0.0

        self.nulltarget = Target()
        self.nulltarget.targetid = -1
        self.nulltarget.num_exp = 1
        self.nulltarget.exp_times = [0.0]
        self.nulltarget.num_props = 1
        self.nulltarget.propid_list = [0]
        self.nulltarget.need_list = [0.0]
        self.nulltarget.bonus_list = [0.0]
        self.nulltarget.value_list = [0.0]
        self.nulltarget.propboost_list = [1.0]

        self.last_winner_target = self.nulltarget.get_copy()
        self.deep_drilling_target = None

        self.need_filter_swap = False
        self.filter_to_unmount = ""
        self.filter_to_mount = ""

        self.cloud = 0.0
        self.seeing = 0.0

    def configure_scheduler(self, **kwargs):
        raise NotImplemented

    def configure_duration(self, survey_duration):

        self.survey_duration_DAYS = survey_duration
        self.survey_duration_SECS = survey_duration * 24 * 60 * 60.0

    def configure(self, confdict):
        self.params.configure(confdict)
        self.log.log(WORDY,
                     "configure: night_boundary=%.1f" % (self.params.night_boundary))

    def configure_location(self, confdict):

        self.location.configure(confdict)
        self.observatoryModel.location.configure(confdict)
        self.observatoryModel2.location.configure(confdict)
        self.sky.update_location(self.location)

    def configure_observatory(self, confdict):
        """This method calls the configure()method in ObservatoryModel class,
        which configures all its submodules. When initializing one can issue
        a call to this method with a complete set of parameters on confdict.

        Parameters
        ----------
        confdict : dict()

        Returns
        -------
        None
        """
        self.observatoryModel.configure(confdict)
        self.observatoryModel2.configure(confdict)

    def configure_telescope(self, confdict):

        self.observatoryModel.configure_telescope(confdict)
        self.observatoryModel2.configure_telescope(confdict)

    def configure_rotator(self, confdict):

        self.observatoryModel.configure_rotator(confdict)
        self.observatoryModel2.configure_rotator(confdict)

    def configure_dome(self, confdict):

        self.observatoryModel.configure_dome(confdict)
        self.observatoryModel2.configure_dome(confdict)

    def configure_optics(self, confdict):

        self.observatoryModel.configure_optics(confdict)
        self.observatoryModel2.configure_optics(confdict)

    def configure_camera(self, confdict):

        self.observatoryModel.configure_camera(confdict)
        self.observatoryModel2.configure_camera(confdict)

    def configure_slew(self, confdict):

        self.observatoryModel.configure_slew(confdict)
        self.observatoryModel2.configure_slew(confdict)

    def configure_park(self, confdict):

        self.observatoryModel.configure_park(confdict)
        self.observatoryModel2.configure_park(confdict)

    def start_survey(self, timestamp, night):
        """This method begins the survey.

        Parameters
        ----------
        timestamp : float
        night : int

        Returns
        -------
        None
        """
        self.start_time = timestamp

        self.log.info("start_survey t=%.6f" % timestamp)

        self.survey_started = True
        self.sky.update(timestamp)
        (sunset, sunrise) = self.sky.get_night_boundaries(self.params.night_boundary)
        self.log.debug("start_survey sunset=%.6f sunrise=%.6f" % (sunset, sunrise))
        # if round(sunset) <= round(timestamp) < round(sunrise):
        if sunset <= timestamp < sunrise:
            self.start_night(timestamp, night)

        self.sunset_timestamp = sunset
        self.sunrise_timestamp = sunrise

    def end_survey(self):
        """This method ends the survey.

        Parameters
        ----------
        None

        Returns
        -------
        None
        """
        self.log.info("end_survey")

    def start_night(self, timestamp, night):
        """This method is called once per night before observations begin.
        It is not called if the observatory is undergoing downtime.
        Parameters
        ----------
        timestamp : float
        night : int

        Returns
        -------
        None
        """
        timeprogress = (timestamp - self.start_time) / self.survey_duration_SECS
        self.log.info("start_night t=%.6f, night=%d timeprogress=%.2f%%" %
                      (timestamp, night, 100 * timeprogress))

        self.isnight = True

    def end_night(self, timestamp, night):
        """This method is called once per night after observing completes.

        Parameters
        ----------
        timestamp : float
        night : int

        Returns
        -------
        None
        """
        pass

    def swap_filter(self, filter_to_unmount, filter_to_mount):

        self.log.info("swap_filter swap %s=>cam=>%s" % (filter_to_mount, filter_to_unmount))

        self.observatoryModel.swap_filter(filter_to_unmount)

        self.unmounted_filter = filter_to_unmount
        self.mounted_filter = filter_to_mount

        return

    def update_time(self, timestamp, night):
            self.time = timestamp
            self.observatoryModel.update_state(self.time)
            if not self.survey_started:
                self.start_survey(timestamp, night)

            if self.isnight:
                # if round(timestamp) >= round(self.sunrise_timestamp):
                if timestamp >= self.sunrise_timestamp:
                    self.end_night(timestamp, night)
            else:
                # if round(timestamp) >= round(self.sunset_timestamp):
                if timestamp >= self.sunset_timestamp:
                    self.start_night(timestamp, night)
            return self.isnight

    def get_need_filter_swap(self):
        """When scheduler determines that a filter swap is needed,
        this shall return a tuple where the first element is a TRUE value,
        and the second and third elements are single-character strings identifying
        which filter to remove from the carousel, and which filter to add, respectively.

        Parameters
        ----------
        None

        Returns
        -------
        Tuple (bool, str, str)
        """
        return (self.need_filter_swap, self.filter_to_unmount, self.filter_to_mount)

    def update_internal_conditions(self, observatory_state, night):

        if observatory_state.unmountedfilters != self.observatoryModel.current_state.unmountedfilters:
            unmount = observatory_state.unmountedfilters[0]
            mount = self.observatoryModel.current_state.unmountedfilters[0]
            self.swap_filter(unmount, mount)
        self.time = observatory_state.time
        self.observatoryModel.set_state(observatory_state)
        self.observatoryState.set(observatory_state)

    def update_external_conditions(self, cloud, seeing):

        self.cloud = cloud
        self.seeing = seeing

        return

    def cold_start(self, observations):
        """Rebuilds the internal state of the scheduler from a list of observations.

        Parameters
        ----------
        observations : list of Observation objects

        Returns
        -------
        None
        """
        raise NotImplemented

    def select_next_target(self):
        """Picks a target and returns it as a target object.

        Parameters
        ----------
        None

        Returns
        -------
        Target
        """

        raise NotImplemented

    def register_observation(self, observation):
        """Validates observation and returns a list of successfully completed observations.

        Parameters
        ----------
        observation : Observation or a python list of Observations

        Returns
        -------
        Python list of one or more Observations
        """

        raise NotImplemented
Example #9
0
 def setUpClass(cls):
     warnings.filterwarnings('ignore', category=FutureWarning, append=True)
     location = ObservatoryLocation()
     location.for_lsst()
     cls.skyModel = AstronomicalSkyModel(location)