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)
def __init__(self, star, observatory, dataset="winter"): super().__init__() self.dataset = dataset self.star = star self.observatory = observatory # Define target parameterss coords = star.coordinates self.target = astroplan.FixedTarget(name=star.name, coord=coords) self.observer = astroplan.Observer(observatory) # Load data tapas_dir = join(dirname(__file__), "../../data/tapas/") tellw, self.airw, self.angw = load_tellurics(join(tapas_dir, "*winter*ipac.gz")) tells, self.airs, self.angs = load_tellurics(join(tapas_dir, "*summer*ipac.gz")) # Sort wavelength axis # We assume here that all files use the same wavelength axis wavew, waves = np.squeeze(tellw[0, :, 0]), np.squeeze(tells[0, :, 0]) iiw, iis = np.argsort(wavew), np.argsort(waves) self.tellw, self.tells = tellw[:, iiw, :], tells[:, iis, :] # Create interpolator self.wavew, self.waves = ( np.squeeze(self.tellw[0, :, 0]), np.squeeze(self.tells[0, :, 0]), ) self.fluxw, self.fluxs = ( np.squeeze(self.tellw[:, :, 1]), np.squeeze(self.tells[:, :, 1]), ) self.tellwi = RegularGridInterpolator((self.airw, self.wavew), self.fluxw) self.tellsi = RegularGridInterpolator((self.airs, self.waves), self.fluxs)
def followup_targets(events, fields, area): """A generator of event/astroplan target pairs given events and fields All ra and dec values should be in decimal degrees. Args: - events :: a pandas.DataFrame with event coordinates in ra and decl columns - fields :: a pandas.DataFrame with field coordinates in fieldRA and fieldDec columns - area :: the search area in square degrees Returns: a succession of event/astroplan.FixedTarget pairs """ event_fields = find_event_fields(events, fields, area) targets = OrderedDict() # there should be a faster (vectorized) way to do this. for idx, event in events.iterrows(): targets = [] for field_idx, field in event_fields.query( f'event_id=={event.event_id}').iterrows(): field_coords = SkyCoord(ra=field.fieldRA * u.deg, dec=field.fieldDec * u.deg) field_name = "%d" % field.fieldID target = astroplan.FixedTarget(coord=field_coords, name=field_name) targets.append(target) yield event, targets
def airmass(self): target = astroplan.FixedTarget(name=self.star.name, coord=self.star.coordinates) observer = astroplan.Observer(self.observatory_location) altaz = observer.altaz(self.datetime, target) airmass = altaz.secz.value return airmass
def _set_fixed_targets(self, row): """ Add a column of SkyCoords to pandas dataframe :return: """ return astroplan.FixedTarget(name=row['objname'], coord=row['SkyCoords'])
def _parse_single_target(target): try: crd = SkyCoord(target) except ValueError: # The coordinate string is ambiguous; make assumptions if target[0].isalpha(): crd = SkyCoord.from_name(target) elif ":" in target: crd = SkyCoord(target, unit="hour,deg") else: crd = SkyCoord(target, unit="deg") return astroplan.FixedTarget(crd)
def __init__(self, star, observatory, degree=2, skip_resample=False): self.star = star self.observatory = observatory # Define target parameterss coords = star.coordinates self.target = astroplan.FixedTarget(name=star.name, coord=coords) self.observer = astroplan.Observer(observatory) self.degree = degree self.skip_resample = skip_resample
def rise_set(source, observatory, elevation_limit=None, when=None, lst=False): """Rise and set times for a source""" if isinstance(source, astroplan.FixedTarget): S = source elif isinstance(source, astropy.coordinates.SkyCoord): S = astroplan.FixedTarget(source) else: S = astroplan.FixedTarget.from_name(source) if isinstance(observatory, astroplan.Observer): L = observatory elif isinstance(observatory, astropy.coordinates.EarthLocation): L = astroplan.Observer(observatory) else: try: L = astroplan.Observer(observatory_location(observatory)) if elevation_limit is None: elevation_limit = known_elevation_limits.get( observatory_aliases[observatory.lower()], None) except ValueError: try: L = astroplan.Observer.from_site(observatory) except ValueError: raise ValueError( "Observatory {!r} not found".format(observatory)) if elevation_limit is None: elevation_limit = 0 * u.deg if when is None: T = astropy.time.Time.now() elif isinstance(when, astropy.time.Time): T = when else: T = astropy.time.Time(when) if L.target_is_up(T, S, horizon=elevation_limit): rise = L.target_rise_time(T, S, which="previous", horizon=elevation_limit) else: rise = L.target_rise_time(T, S, which="next", horizon=elevation_limit) set = L.target_set_time(rise, S, which="next", horizon=elevation_limit) rise.location = L.location set.location = L.location rise.format = "iso" set.format = "iso" if lst: return _rise_set( rise=format_sidereal_time(rise.sidereal_time("apparent")), set=format_sidereal_time(set.sidereal_time("apparent")), ) else: return _rise_set(rise=rise, set=set)
def get_segments_tile(config_struct, observatory, radec, segmentlist): observer = astroplan.Observer(location=observatory) fxdbdy = astroplan.FixedTarget(coord=radec) date_start = Time(segmentlist[0][0], format='mjd', scale='utc') date_end = Time(segmentlist[-1][1], format='mjd', scale='utc') tilesegmentlist = segments.segmentlist() while date_start.mjd < date_end.mjd: date_rise = observer.target_rise_time(date_start, fxdbdy) date_set = observer.target_set_time(date_start, fxdbdy) print(date_rise.mjd, date_set.mjd) if (date_rise.mjd < 0) and (date_set.mjd < 0): break print(date_rise.mjd, date_set.mjd) if date_rise > date_set: date_rise = observer.target_rise_time( date_start - TimeDelta(24 * u.hour), fxdbdy) print(date_rise.mjd, date_set.mjd) segment = segments.segment(date_rise.mjd, date_set.mjd) tilesegmentlist = tilesegmentlist + segments.segmentlist([segment]) tilesegmentlist.coalesce() date_start = date_set + TimeDelta(24 * u.hour) #moonsegmentlist = get_skybrightness(\ # config_struct,segmentlist,observer,fxdbdy,radec) moonsegmentlist = get_moon_segments(\ config_struct,segmentlist,observer,fxdbdy,radec) tilesegmentlistdic = segments.segmentlistdict() tilesegmentlistdic["observations"] = segmentlist tilesegmentlistdic["tile"] = tilesegmentlist tilesegmentlistdic["moon"] = moonsegmentlist tilesegmentlist = tilesegmentlistdic.intersection( ["observations", "tile", "moon"]) tilesegmentlist.coalesce() return tilesegmentlist
def calcTargetLimits(self, X=1.5): """ Calculate the limites of each target candidates Input: X - airmass cutoff limit Computes: S - Start time target is above X (units: JD) [Ntargs] F - Set time of target below X (units: JD) [Ntargs] alt - array of alts for target [Ntimesteps, Ntargs] """ Ntargs = len(self.dat.ids) # Set up arrays alt = np.zeros((len(self.time_window), Ntargs)) # altitudes of targets S = np.zeros(Ntargs) # Start time above X F = np.zeros(Ntargs) # Time sets below X for star in range(0, Ntargs): tempcoords = SkyCoord(self.dat.RA[star], self.dat.DEC[star], frame='icrs', unit=(u.hourangle, u.deg)) temptarg = astroplan.FixedTarget(name=self.dat.ids[star], coord=tempcoords) self.whipple = astroplan.Observer.at_site('whipple') alt[:, star] = self.whipple.altaz(Time(self.time_window, format='jd'), temptarg).alt max_alt = np.arccos(1.0 / X) * 180.0 / np.pi star_up = np.where(alt[:, star] > 40.0)[0] if len(star_up) != 0: S[star] = self.time_window[star_up][0] #star starts being up F[star] = self.time_window[star_up][ -1] #star sets below 30 deg else: S[star] = 9.e9 F[star] = -9.e9 self.S = S self.F = F self.alt = alt
def target(self): """Representation of the RA and Dec of this Obj as an astroplan.FixedTarget.""" coord = ap_coord.SkyCoord(self.ra, self.dec, unit='deg') return astroplan.FixedTarget(name=self.id, coord=coord)
def single_planet(data, date_start, date_end, constraints, observer, verbose=0): """ Performs the observability and SNR estimation for a single planet Parameters ---------- data : dict data for this planet date_start : Time start date date_end : Time end date constraints : list list of observability constraints observer : astroplan.Observer telescope location verbose : int, optional how much information to print, by default 0 Returns ------- result : dict contains everything about this planet, one entry per observable transit """ set_verbose_level(verbose) primary_eclipse_time = Time(data["pl_tranmid"], format="jd") orbital_period = data["pl_orbper"] * u.day eclipse_duration = data["pl_trandur"] * u.hour name = data["pl_name"] # Update the progress bar # Define the target coordinates coord = SkyCoord(ra=data["ra"], dec=data["dec"], unit=u.deg) target = ap.FixedTarget(coord, name=name) # Define the star-planet system system = ap.EclipsingSystem( primary_eclipse_time=primary_eclipse_time, orbital_period=orbital_period, duration=eclipse_duration, name=name, ) # Find all eclipses of this system n_max_eclipses = (date_end - date_start) / orbital_period n_max_eclipses = max(n_max_eclipses, 1) eclipses = system.next_primary_ingress_egress_time( date_start, n_eclipses=n_max_eclipses) # If the last eclipse is past the final date, just cut it off if eclipses[-1][0] > date_end: eclipses = eclipses[:-1] if len(eclipses) == 0: logger.warning(f"No observable transits found for planet {name}") return None # Check if they are observable is_observable = ap.is_event_observable(constraints, observer, target, times_ingress_egress=eclipses)[0] observable_eclipses = eclipses[is_observable] n_eclipses = len(observable_eclipses) if n_eclipses == 0: logger.warning(f"No observable transits found for planet {name}") return None # Calculate the SNR for those that are left magnitude = data["sy_kmag"] * u.mag exptime = eclipse_duration # exptime = np.diff(observable_eclipses.jd, axis=1).ravel() * u.day obstime = Time(np.mean(observable_eclipses.jd, axis=1), format="jd") airmass = calculate_airmass(obstime, observer, target) snr = estimate_snr(magnitude, exptime, airmass) # Save results for later result = { "name": [name] * n_eclipses, "snr": snr.to_value(1), "exptime": [eclipse_duration.to_value(u.second)] * n_eclipses, "time": obstime.mjd, "time_begin": observable_eclipses[:, 0].datetime, "time_end": observable_eclipses[:, 1].datetime, "stellar_effective_temperature": [data["st_teff"]] * n_eclipses, } return result
def get_skybrightness(config_struct, segmentlist, observer, fxdbdy, radec): moonsegmentlist = segments.segmentlist() if config_struct["filt"] == "c": passband = "g" else: passband = config_struct["filt"] # Moon phase data (from Coughlin, Stubbs, and Claver Table 2) moon_phases = [2, 10, 45, 90] moon_data = { 'u': [2.7, 3.1, 4.2, 5.7], 'g': [2.4, 2.8, 3.8, 5.2], 'r': [2.1, 2.5, 3.4, 4.9], 'i': [1.9, 2.3, 3.3, 4.7], 'z': [1.9, 2.2, 3.2, 4.6], 'y': [1.8, 2.2, 3.1, 4.5] } # Determine moon data for this phase moon_data_passband = moon_data[passband] # Fits to solar sky brightness (from Coughlin, Stubbs, and Claver Table 4) sun_data = { 'u': [88.5, -0.5, -0.5, 0.4], 'g': [386.5, -2.2, -2.4, 0.8], 'r': [189.0, -1.4, -1.1, 0.8], 'i': [164.8, -1.5, -0.7, 0.6], 'z': [231.2, -2.8, -0.7, 1.4], 'zs': [131.1, -1.4, -0.5, 0.2], 'y': [92.0, -1.3, -0.2, 0.9] } sun_data_error = { 'u': [6.2, 0.1, 0.1, 0.1], 'g': [34.0, 0.2, 0.2, 0.5], 'r': [32.7, 0.2, 0.2, 0.5], 'i': [33.1, 0.2, 0.2, 0.5], 'z': [62.3, 0.3, 0.4, 0.9], 'zs': [45.6, 0.2, 0.3, 0.6], 'y': [32.7, 0.2, 0.2, 0.5] } # Determine sun data for this phase sun_data_passband = sun_data[passband] dt = 6.0 / 24.0 tt = np.arange(segmentlist[0][0], segmentlist[-1][1] + dt, dt) fxdbdy = astroplan.FixedTarget(coord=radec) ra2 = radec.ra.radian d2 = radec.dec.radian # Where is the moon? for ii in range(len(tt) - 1): date_start = Time(tt[ii], format='mjd', scale='utc') alt_target = observer.altaz(date_start, fxdbdy).alt.deg az_target = observer.altaz(date_start, fxdbdy).az.deg #print("Altitude / Azimuth of target: %.5f / %.5f"%(alt_target,az_target)) alt_moon = observer.moon_altaz(date_start).alt.deg az_moon = observer.moon_altaz(date_start).az.deg #print("Altitude / Azimuth of moon: %.5f / %.5f"%(alt_moon,az_moon)) if (alt_target < 30.0) or (alt_moon < 30.0): total_mag, total_mag_error, flux_mag, flux_mag_error = np.inf, np.inf, np.inf, np.inf else: # Coverting both target and moon ra and dec to radians ra1 = astropy.coordinates.get_moon(date_start).ra.radian d1 = astropy.coordinates.get_moon(date_start).dec.radian # Calculate angle between target and moon cosA = np.sin(d1) * np.sin(d2) + np.cos(d1) * np.cos(d2) * np.cos( ra1 - ra2) angle = np.arccos(cosA) * (360 / (2 * np.pi)) #print("Angle between moon and target: %.5f"%(angle)) moon_phase = np.mod( observer.moon_phase(date_start).value * (360 / (2 * np.pi)), 90) delta_mag = np.interp(moon_phase, moon_phases, moon_data_passband) delta_mag_error = 0.1 * delta_mag flux = sun_data_passband[0] + sun_data_passband[1]*angle +\ sun_data_passband[2]*alt_target + sun_data_passband[3]*alt_moon flux_zp = sun_data_passband[0] + sun_data_passband[1]*90.0 +\ sun_data_passband[2]*90.0 + sun_data_passband[3]*45.0 # check if flux < 0: too small to fit if flux < 0: flux = 1e-10 flux = flux * (10**11) flux_zp = flux_zp * (10**11) flux_mag = -2.5 * (np.log10(flux) - np.log10(flux_zp)) sun_data_passband_error = sun_data_error[passband] flux_error = np.sqrt(sun_data_passband_error[0]**2 + sun_data_passband_error[1]**2 * angle**2 +\ sun_data_passband_error[2]**2 * alt_target**2 + sun_data_passband_error[3]**2 * alt_moon**2) flux_error = flux_error * (10**11) flux_mag_error = 1.08574 * flux_error / flux # Determine total magnitude contribution total_mag = delta_mag + flux_mag total_mag_error = np.sqrt(delta_mag_error**2 + flux_mag_error**2) #print(tt[ii], angle, alt_target, alt_moon, total_mag, total_mag_error) if total_mag > 0.0: segment = segments.segment(tt[ii], tt[ii + 1]) moonsegmentlist = moonsegmentlist + segments.segmentlist([segment]) moonsegmentlist.coalesce() #else: # print(tt[ii], angle, alt_target, alt_moon, total_mag, total_mag_error) moonsegmentlistdic = segments.segmentlistdict() moonsegmentlistdic["observations"] = segmentlist moonsegmentlistdic["moon"] = moonsegmentlist moonsegmentlist = moonsegmentlistdic.intersection(["observations", "moon"]) moonsegmentlist.coalesce() #print("Keeping %.2f %% of data"%(100.0*np.sum(np.diff(moonsegmentlist))/np.sum(np.diff(segmentlist)))) return moonsegmentlist
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')
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)
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