Ejemplo n.º 1
0
    def get_next_observable_target(self, target_list, obs_time, max_time=-1,
                                   airmass=(1, 2.2),  moon_sep=(30, 180),
                                   block_type=''):
        """
        
        :return: 
        """

        # If the target_list is empty then all we can do is return back no
        # target and do a standard for the time being
        next_target = False

        if target_list.empty:
            return next_target, ""

        for row in target_list.itertuples():

            constraint = astroplan.AirmassConstraint(min=airmass[0],
                                                     max=airmass[1])

            if row.typedesig == 'f':
                if astroplan.is_observable(constraint, self.obs_site,
                                           row.FixedObject,
                                           times=[obs_time]):

                    return_dict = {'name': row.objname,
                                   'priority': row.priority}

                    return row.req_id, {**row.obs_dict, **return_dict}

        return False, False
def get_default_constraints():
    """
    Defines the default constraints if none are explicitly set.
    Those are Altitude above 30 deg, airmass less than 1.7, 
    astronomical twilight at the observing location, and at 
    least 45 deg seperation from the moon

    Returns
    -------
    constraints : list
        list of constraints
    """
    # Altitude constraints definition
    altcons = ap.AltitudeConstraint(min=+30 * u.deg, max=None)

    # Airmass constraints definition
    airmasscons = ap.AirmassConstraint(min=None, max=1.7)

    # Astronomical Nighttime constraints definition:
    # begin and end of each night at paranal as
    # AtNightConstraint.twilight_astronomical
    night_cons_per_night = ap.AtNightConstraint.twilight_astronomical()

    # Moon Constraint
    mooncons = ap.MoonSeparationConstraint(min=+45 * u.deg, max=None)

    constraints = [night_cons_per_night, altcons, airmasscons, mooncons]
    return constraints
Ejemplo n.º 3
0
    def test_transitioner(self):
        pachon = astroplan.Observer.at_site("Cerro Pachon")
        start_time = astropy.time.Time(59598.66945625747, format='mjd')
        readout_time = 2 * u.second
        exptime = 15 * u.second
        nexp = 2

        targets = [
            astroplan.FixedTarget(coord=SkyCoord(ra=81.1339111328125 * u.deg,
                                                 dec=-17.532396316528303 *
                                                 u.deg),
                                  name="1857"),
            astroplan.FixedTarget(coord=SkyCoord(ra=81.1758499145508 * u.deg,
                                                 dec=-12.2103328704834 *
                                                 u.deg),
                                  name="2100")
        ]

        constraints = [
            astroplan.AltitudeConstraint(30 * u.deg, 89 * u.deg),
            astroplan.AirmassConstraint(2.2),
            astroplan.AtNightConstraint.twilight_astronomical()
        ]
        obs_blocks = [
            astroplan.ObservingBlock.from_exposures(
                target,
                1,
                exptime,
                nexp,
                readout_time,
                configuration={'filter': band},
                constraints=constraints) for band in ('g', 'i')
            for target in targets
        ]

        transitioner = apsupp.LSSTTransitioner()
        for old_block, new_block in zip(obs_blocks[:-1], obs_blocks[1:]):
            block = transitioner(old_block, new_block, start_time, pachon)
            duration_seconds = (block.duration / u.second).value

            old_filter = old_block.configuration['filter']
            new_filter = new_block.configuration['filter']
            if old_filter == new_filter:
                min_expected_duration = 2
            else:
                min_expected_duration = 120

            self.assertGreaterEqual(duration_seconds, min_expected_duration)
            self.assertLess(duration_seconds, 600)
Ejemplo n.º 4
0
delta_midnight = np.linspace(-12, 12, 1000) * u.hour
times_July12_to_13 = midnight + delta_midnight
frame_July12_to_13 = AltAz(obstime=times_July12_to_13, location=paranal_loc)
sunaltazs_July12_to_13 = get_sun(times_July12_to_13).transform_to(
    frame_July12_to_13)

GJ9827baltaz = GJ9827b.transform_to(AltAz(obstime=time, location=paranal_loc))

delta_midnight = np.linspace(-2, 10, 100) * u.hour
obstime = midnight + delta_midnight

paranal = astroplan.Observer(paranal_loc, timezone='Etc/GMT-4')

Altcons = astroplan.AltitudeConstraint(min=+30 * u.deg, max=None)
Airmasscons = astroplan.AirmassConstraint(min=None, max=3.0)

Alt_constraints = Altcons.compute_constraint(times=obstime,
                                             observer=paranal,
                                             targets=GJ9827b)
Airmass_constraints = Airmasscons.compute_constraint(times=obstime,
                                                     observer=paranal,
                                                     targets=GJ9827b)

observable = astroplan.is_observable(constraints=[Altcons, Airmasscons],
                                     observer=paranal,
                                     targets=GJ9827b,
                                     times=obstime)

midnight1 = Time('2020-7-13 00:00:00') - utcoffset
midnight2 = Time('2021-1-13 00:00:00') - utcoffset
Ejemplo n.º 5
0
def schedule_followup(events, fields, config):
    """Schedule follow-up for a given set of events.

    Args:
       - events :: a pandas.DataFrame of events
       - fields :: a pandas.DataFrame of fields for followup
       - config :: a ConfigParser with follow-up configuration data.

    Returns:
       a pandas.DataFrame with the schedule of follow-up visits.
    """
    readout_time = float(config['instrument']['readout_time']) * u.second
    shutter_time = float(config['instrument']['shutter_time']) * u.second
    exptime = float(config['obs_block']['exptime']) * u.second
    nexp = int(config['obs_block']['nexp'])
    bands = config['search']['bands'].split()

    #
    # Create an astroplan.Observer
    #
    observer = astroplan.Observer.at_site(config['site']['name'])

    #
    # Create a Transitioner
    #
    slew_src = SlewTimeSource()
    transitioner = apsupp.OpsimTransitioner(slew_src)

    #
    # Build astroplan constraints
    #
    alt_constraint = astroplan.AltitudeConstraint(
        float(config['constraints']['min_alt']) * u.deg,
        float(config['constraints']['max_alt']) * u.deg)
    airmass_constraint = astroplan.AirmassConstraint(
        float(config['constraints']['max_airmass']))

    constraints = [alt_constraint, airmass_constraint]

    twilight_type = config['constraints']['twilight']
    if twilight_type == 'nautical':
        constraints.append(astroplan.AtNightConstraint.twilight_nautical())
    elif twilight_type == 'civil':
        constraints.append(astroplan.AtNightConstraint.twilight_civil())
    elif twilight_type == 'astronomical':
        constraints.append(astroplan.AtNightConstraint.twilight_astronomical())
    else:
        raise NotImplementedError

    #
    # Create an astroplan.Scheduler
    #
    time_resolution = float(config['scheduler']['time_resolution']) * u.second
    scheduler = DirectScheduler(constraints=constraints,
                                observer=observer,
                                transitioner=transitioner,
                                time_resolution=time_resolution)

    # helper function to turn targets into blocks
    def obsblock(target, band):
        block = astroplan.ObservingBlock.from_exposures(
            target,
            1,
            exptime,
            nexp,
            readout_time,
            configuration={'filter': band})
        block.shutter_time = shutter_time
        block.duration = block.duration \
                         + block.number_exposures * block.shutter_time
        return block

    # Schedule follow-up for each event
    latest_visit_end = astropy.time.Time(0, format='mjd')
    search_area = float(config['search']['area'])
    scan_schedules = []
    for event, targets in followup_targets(events, fields, search_area):
        event_time = astropy.time.Time(np.min(event.mjd.min()), format='mjd')
        trigger_time = event_time \
                       + float(config['search']['trigger_delay'])*u.second
        info(
            f'Scheduling event {event.event_id} at {event.ra}, {event.decl} on {event_time.iso}'
        )

        # Only work on one event at a time
        if trigger_time < latest_visit_end:
            continue

        # Attempt the first strategy -- two scans the area in one
        # band, one hour apart, within 12 hours of the event.
        # If this isn't possible, two scans with larger separation,
        # in two bands each.
        try:
            obsblocks = [obsblock(t, bands[0]) for t in targets]
            schedule = two_scan_schedule(scheduler,
                                         obsblocks,
                                         trigger_time,
                                         event_time,
                                         obs_window=12 * u.hour,
                                         scan_separation=1 * u.hour)
        except StrategyFailed:
            obsblocks = [obsblock(t, b) for b in bands for t in targets]
            schedule = two_scan_schedule(scheduler,
                                         obsblocks,
                                         trigger_time,
                                         event_time,
                                         obs_window=24 * u.hour,
                                         scan_separation=1 * u.hour,
                                         ignore_failure=True)

        scan_schedules.append(schedule)

    schedule = pd.concat([s for s in scan_schedules], axis=0)

    schedule['proposals'] = config['scheduler']['proposals']
    return schedule
Ejemplo n.º 6
0
    def test_schedule(self):
        pachon = astroplan.Observer.at_site("Cerro Pachon")
        start_time = astropy.time.Time(59598.66945625747, format='mjd')
        readout_time = 2 * u.second
        exptime = 15 * u.second
        nexp = 2
        slew_rate = 0.5 * (0.66 + 0.57) * u.deg / u.second
        filter_change_time = {'filter': {'default': 120 * u.second}}

        transitioner = astroplan.Transitioner(slew_rate, filter_change_time)

        targets = [
            astroplan.FixedTarget(coord=SkyCoord(ra=81.1339111328125 * u.deg,
                                                 dec=-17.532396316528303 *
                                                 u.deg),
                                  name="1857"),
            astroplan.FixedTarget(coord=SkyCoord(ra=81.1758499145508 * u.deg,
                                                 dec=-12.2103328704834 *
                                                 u.deg),
                                  name="2100")
        ]

        constraints = [
            astroplan.AltitudeConstraint(30 * u.deg, 89 * u.deg),
            astroplan.AirmassConstraint(2.2),
            astroplan.AtNightConstraint.twilight_astronomical()
        ]
        obsblocks = [
            astroplan.ObservingBlock.from_exposures(
                target,
                1,
                exptime,
                nexp,
                readout_time,
                configuration={'filter': band},
                constraints=constraints) for band in ('g', 'i')
            for target in targets
        ]

        scheduler = apsupp.DirectScheduler(constraints=constraints,
                                           observer=pachon,
                                           transitioner=transitioner,
                                           time_resolution=1 * u.hour)
        schedule = astroplan.scheduling.Schedule(start_time,
                                                 start_time + 1 * u.day)
        scheduler(obsblocks, schedule)

        self.assertIsInstance(schedule.slots[1].block.target,
                              astroplan.FixedTarget)
        self.assertEqual(schedule.slots[1].block.target.name, '1857')
        self.assertIsInstance(schedule.slots[2].block,
                              astroplan.TransitionBlock)
        self.assertIsInstance(schedule.slots[3].block.target,
                              astroplan.FixedTarget)
        self.assertEqual(schedule.slots[3].block.target.name, '2100')
        self.assertIsInstance(schedule.slots[4].block,
                              astroplan.TransitionBlock)
        self.assertIsInstance(schedule.slots[5].block.target,
                              astroplan.FixedTarget)
        self.assertEqual(schedule.slots[5].block.target.name, '1857')
        self.assertIsInstance(schedule.slots[6].block,
                              astroplan.TransitionBlock)
        self.assertIsInstance(schedule.slots[7].block.target,
                              astroplan.FixedTarget)
        self.assertEqual(schedule.slots[7].block.target.name, '2100')
Ejemplo n.º 7
0
    def test_transitioner(self):
        observatory = ObservatoryModel()
        observatory.configure_from_module()
        pachon = astroplan.Observer.at_site("Cerro Pachon")
        start_time = astropy.time.Time(59598.66945625747, format='mjd')
        readout_time = observatory.params.readouttime * u.second
        exptime = 15 * u.second
        nexp = 2

        targets = [
            astroplan.FixedTarget(coord=SkyCoord(ra=81.1339111328125 * u.deg,
                                                 dec=-17.532396316528303 *
                                                 u.deg),
                                  name="1857"),
            astroplan.FixedTarget(coord=SkyCoord(ra=81.1758499145508 * u.deg,
                                                 dec=-12.2103328704834 *
                                                 u.deg),
                                  name="2100")
        ]

        constraints = [
            astroplan.AltitudeConstraint(30 * u.deg, 89 * u.deg),
            astroplan.AirmassConstraint(2.2),
            astroplan.AtNightConstraint.twilight_astronomical()
        ]
        obs_blocks = [
            astroplan.ObservingBlock.from_exposures(
                target,
                1,
                exptime,
                nexp,
                readout_time,
                configuration={'filter': band},
                constraints=constraints) for band in ('g', 'i')
            for target in targets
        ]

        self.assertGreater(observatory.params.readouttime, 1.5)
        self.assertGreater(observatory.params.filter_changetime, 15.0)

        slew_src = SlewTimeSource()
        transitioner = apsupp.OpsimTransitioner(slew_src)
        whitepaper_transitioner = apsupp.LSSTTransitioner()
        for old_block, new_block in zip(obs_blocks[:-1], obs_blocks[1:]):
            block = transitioner(old_block, new_block, start_time, pachon)
            wp_block = whitepaper_transitioner(old_block, new_block,
                                               start_time, pachon)
            duration_seconds = (block.duration / u.second).value
            wp_duration_seconds = (wp_block.duration / u.second).value

            self.assertAlmostEqual(duration_seconds,
                                   wp_duration_seconds,
                                   delta=5)

            old_filter = old_block.configuration['filter']
            new_filter = new_block.configuration['filter']
            if old_filter == new_filter:
                min_expected_duration = observatory.params.readouttime
            else:
                min_expected_duration = max(
                    observatory.params.readouttime,
                    observatory.params.filter_changetime)

            self.assertGreaterEqual(duration_seconds, min_expected_duration)
            self.assertLess(duration_seconds, 600)
Ejemplo n.º 8
0
    def __init__(
        self,
        name: str,
        ra: float = None,
        dec: float = None,
        arrivaltime: str = None,
        date: str = None,
        max_airmass=2.0,
        observationlength: float = 300,
        bands: list = ["g", "r"],
        alertsource: str = None,
        verbose: bool = True,
        **kwargs,
    ):

        self.name = name
        self.arrivaltime = arrivaltime
        self.alertsource = alertsource
        self.max_airmass = max_airmass
        self.observationlength = observationlength
        self.bands = bands
        self.ra_err = None
        self.dec_err = None
        self.warning = None
        self.observable = True
        self.rejection_reason = None
        self.datasource = None
        self.found_in_archive = False
        self.search_full_archive = False

        if ra is None and self.alertsource in icecube:
            if verbose:
                print("Parsing an IceCube alert")

            # Check if request is archival:
            archive, latest_archive_no = gcn_parser.get_gcn_circulars_archive()

            # Check if the alert is younger than latest archive entry
            archival_names = [entry[0] for entry in archive]
            archival_dates = [int(entry[2:-1]) for entry in archival_names]
            latest_archival = max(archival_dates)
            this_alert_date = int(self.name[2:-1])
            if this_alert_date > latest_archival:
                if verbose:
                    print(
                        "Alert too new, no GCN circular available yet. Using latest GCN notice"
                    )
            else:
                if verbose:
                    print("Alert info should be in GCN circular archive")
                self.search_full_archive = True

            if self.search_full_archive:
                self.search_match_in_archive(archive)

                # Well, if it's not in the latest archive, use the full
                # backwards search
                while self.found_in_archive is False:
                    archive, _ = gcn_parser.get_gcn_circulars_archive(latest_archive_no)
                    self.search_match_in_archive(archive)
                    latest_archive_no -= 1

            if self.found_in_archive:
                gcn_info = gcn_parser.parse_gcn_circular(self.gcn_nr)
                self.ra = gcn_info["ra"]
                self.ra_err = gcn_info["ra_err"]
                self.dec = gcn_info["dec"]
                self.dec_err = gcn_info["dec_err"]
                self.arrivaltime = gcn_info["time"]

            else:
                if verbose:
                    print("No archival GCN circular found. Using newest notice!")
                (
                    ra_notice,
                    dec_notice,
                    self.arrivaltime,
                    revision,
                ) = gcn_parser.parse_latest_gcn_notice()
                gcn_nr_latest = archive[0][1]
                gcn_info = gcn_parser.parse_gcn_circular(gcn_nr_latest)
                ra_circ = gcn_info["ra"]
                ra_err_circ = gcn_info["ra_err"]
                dec_circ = gcn_info["dec"]
                dec_err_circ = gcn_info["dec_err"]
                coords_notice = SkyCoord(
                    ra_notice * u.deg, dec_notice * u.deg, frame="icrs"
                )
                coords_circular = SkyCoord(
                    ra_circ * u.deg, dec_circ * u.deg, frame="icrs"
                )
                separation = coords_notice.separation(coords_circular).deg
                if separation < 1:
                    self.ra = ra_circ
                    self.dec = dec_circ
                    self.ra_err = ra_err_circ
                    self.dec_err = dec_err_circ
                    self.datasource = f"GCN Circular {gcn_nr_latest}\n"
                else:
                    self.ra = ra_notice
                    self.dec = dec_notice
                    self.datasource = f"GCN Notice (Rev. {revision})\n"

        elif ra is None and self.alertsource in ztf:
            if is_ztf_name(name):
                print(f"{name} is a ZTF name. Looking in Fritz database for ra/dec")
                from ztf_plan_obs.fritzconnector import FritzInfo

                fritz = FritzInfo([name])

                self.ra = fritz.queryresult["ra"]
                self.dec = fritz.queryresult["dec"]

                self.datasource = "Fritz\n"

                if np.isnan(self.ra):
                    raise ValueError("Object apparently not found on Fritz")

                print("\nFound ZTF object information on Fritz")
        elif ra is None:
            raise ValueError("Please enter ra and dec")

        else:
            self.ra = ra
            self.dec = dec

        self.coordinates = SkyCoord(self.ra * u.deg, self.dec * u.deg, frame="icrs")
        self.coordinates_galactic = self.coordinates.galactic
        self.target = ap.FixedTarget(name=self.name, coord=self.coordinates)
        self.site = Observer.at_site("Palomar", timezone="US/Pacific")
        #'Roque de los Muchachos'
        self.now = Time(datetime.utcnow())
        self.date = date

        if self.date is not None:
            self.start_obswindow = Time(self.date + " 00:00:00.000000")

        else:
            self.start_obswindow = Time(
                str(self.now.datetime.date()) + " 00:00:00.000000"
            )

        self.end_obswindow = Time(self.start_obswindow.mjd + 1, format="mjd").iso

        constraints = [
            ap.AltitudeConstraint(20 * u.deg, 90 * u.deg),
            ap.AirmassConstraint(max_airmass),
            ap.AtNightConstraint.twilight_astronomical(),
        ]

        # Obtain moon coordinates at Palomar for the full time window
        times = Time(self.start_obswindow + np.linspace(0, 24, 1000) * u.hour)
        moon_times = Time(self.start_obswindow + np.linspace(0, 24, 50) * u.hour)
        moon_coords = []

        for time in moon_times:
            moon_coord = astropy.coordinates.get_moon(
                time=time, location=self.site.location
            )
            moon_coords.append(moon_coord)
        self.moon = moon_coords

        airmass = self.site.altaz(times, self.target).secz
        airmass = np.ma.array(airmass, mask=airmass < 1)
        airmass = airmass.filled(fill_value=99)
        airmass = [x.value for x in airmass]

        self.twilight_evening = self.site.twilight_evening_astronomical(
            Time(self.start_obswindow), which="next"
        )
        self.twilight_morning = self.site.twilight_morning_astronomical(
            Time(self.start_obswindow), which="next"
        )

        indices_included = []
        airmasses_included = []
        times_included = []

        for index, t_mjd in enumerate(times.mjd):
            if (
                t_mjd > self.twilight_evening.mjd + 0.01
                and t_mjd < self.twilight_morning.mjd - 0.01
            ):
                if airmass[index] < 2.0:
                    indices_included.append(index)
                    airmasses_included.append(airmass[index])
                    times_included.append(times[index])

        if len(airmasses_included) == 0:
            self.observable = False
            self.rejection_reason = "airmass"

        if np.abs(self.coordinates_galactic.b.deg) < 10:
            self.observable = False
            self.rejection_reason = "proximity to gal. plane"

        self.g_band_recommended_time_start = None
        self.g_band_recommended_time_end = None
        self.r_band_recommended_time_start = None
        self.r_band_recommended_time_end = None

        if self.observable:
            min_airmass = np.min(airmasses_included)
            min_airmass_index = np.argmin(airmasses_included)
            min_airmass_time = times_included[min_airmass_index]

            distance_to_evening = min_airmass_time.mjd - self.twilight_evening.mjd
            distance_to_morning = self.twilight_morning.mjd - min_airmass_time.mjd

            if distance_to_morning < distance_to_evening:
                if "g" in self.bands:
                    self.g_band_recommended_time_start = round_time(
                        min_airmass_time - self.observationlength * u.s - 0.5 * u.hour
                    )
                    self.g_band_recommended_time_end = (
                        self.g_band_recommended_time_start
                        + self.observationlength * u.s
                    )
                if "r" in self.bands:
                    self.r_band_recommended_time_start = round_time(
                        min_airmass_time - self.observationlength * u.s
                    )
                    self.r_band_recommended_time_end = (
                        self.r_band_recommended_time_start
                        + self.observationlength * u.s
                    )

            else:
                if "g" in self.bands:
                    self.g_band_recommended_time_start = round_time(
                        min_airmass_time + self.observationlength * u.s + 0.5 * u.hour
                    )
                    self.g_band_recommended_time_end = (
                        self.g_band_recommended_time_start
                        + self.observationlength * u.s
                    )
                if "r" in self.bands:
                    self.r_band_recommended_time_start = round_time(
                        min_airmass_time + self.observationlength * u.s
                    )
                    self.r_band_recommended_time_end = (
                        self.r_band_recommended_time_start
                        + self.observationlength * u.s
                    )
        if self.alertsource in icecube:
            summarytext = f"Name = IceCube-{self.name[2:]}\n"
        else:
            summarytext = f"Name = {self.name}\n"

        if self.ra_err is not None:
            summarytext += f"RA = {self.coordinates.ra.deg} + {self.ra_err[0]} - {self.ra_err[1]*-1}\nDec = {self.coordinates.dec.deg} + {self.dec_err[0]} - {self.dec_err[1]*-1}\n"
        else:
            summarytext += f"RADEC = {self.coordinates.ra.deg:.8f} {self.coordinates.dec.deg:.8f}\n"

        if self.datasource is not None:
            summarytext += f"Data source: {self.datasource}"

        if self.observable:
            summarytext += (
                f"Minimal airmass ({min_airmass:.2f}) at {min_airmass_time}\n"
            )
        summarytext += f"Separation from galactic plane: {self.coordinates_galactic.b.deg:.2f} deg\n"

        if self.observable:
            summarytext += "Recommended observation times:\n"
            if "g" in self.bands:
                gbandtext = f"g-band: {short_time(self.g_band_recommended_time_start)} - {short_time(self.g_band_recommended_time_end)} [UTC]"
            if "r" in self.bands:
                rbandtext = f"r-band: {short_time(self.r_band_recommended_time_start)} - {short_time(self.r_band_recommended_time_end)} [UTC]"

            if (
                "g" in bands
                and "r" in bands
                and self.g_band_recommended_time_start
                < self.r_band_recommended_time_start
            ):
                bandtexts = [gbandtext + "\n", rbandtext]
            elif (
                "g" in bands
                and "r" in bands
                and self.g_band_recommended_time_start
                > self.r_band_recommended_time_start
            ):
                bandtexts = [rbandtext + "\n", gbandtext]
            elif "g" in bands and "r" not in bands:
                bandtexts = [gbandtext]
            else:
                bandtexts = [rbandtext]

            for item in bandtexts:
                summarytext += item

        if verbose:
            print(summarytext)

        if not os.path.exists(self.name):
            os.makedirs(self.name)

        self.summarytext = summarytext