def test_helio_jd(self): fn = "helio_jd.test" good, dat = self.getData(fn) self.assertEqual(good, True) err = 0 for i in xrange(len(dat[::,0])): hjd = at.helio_jd(dat[i,1], dat[i,2], dat[i,3]) if abs((hjd-dat[i,0])/hjd) > self.p: print "Problem HJD: ", abs((hjd-dat[i,0])/hjd) print hjd, dat[i,0] print "" err += 1 continue self.assertEqual(err, 0)
def helcorr(obs_long, obs_lat, obs_alt, ra2000, dec2000, jd, debug=False): """ Calculate barycentric velocity correction. This function calculates the motion of an observer in the direction of a star. In contract to :py:func:`baryvel` and :py:func:`baryCorr`, the rotation of the Earth is taken into account. .. note:: This function was ported from the REDUCE IDL package. See Piskunov & Valenti 2002, A&A 385, 1095 for a detailed description of the package and/or visit http://www.astro.uu.se/~piskunov/RESEARCH/REDUCE/ .. warning:: Contrary to the original implementation the longitude increases toward the East and the right ascension is given in degrees instead of hours. The JD is given as is, in particular, nothing needs to be subtracted. Parameters ---------- obs_long : float Longitude of observatory (degrees, **eastern** direction is positive) obs_lat : float Latitude of observatory [deg] obs_alt : float Altitude of observatory [m] ra2000 : float Right ascension of object for epoch 2000.0 [deg] dec2000 : float Declination of object for epoch 2000.0 [deg] jd : float Julian date for the middle of exposure. Returns ------- Barycentric correction : float The barycentric correction accounting for the rotation of the Earth, the rotation of the Earth's center around the Earth-Moon barycenter, and the motion of the Earth-Moon barycenter around the center of the Sun [km/s]. HJD : float Heliocentric Julian date for middle of exposure. Notes ----- :IDL REDUCE - Documentation: Calculates heliocentric Julian date, barycentric and heliocentric radial velocity corrections from: INPUT: <OBSLON> Longitude of observatory (degrees, western direction is positive) <OBSLAT> Latitude of observatory (degrees) <OBSALT> Altitude of observatory (meters) <RA2000> Right ascension of object for epoch 2000.0 (hours) <DE2000> Declination of object for epoch 2000.0 (degrees) <JD> Julian date for the middle of exposure [DEBUG=] set keyword to get additional results for debugging OUTPUT: <CORRECTION> barycentric correction - correction for rotation of earth, rotation of earth center about the earth-moon barycenter, earth-moon barycenter about the center of the Sun. <HJD> Heliocentric Julian date for middle of exposure Algorithms used are taken from the IRAF task noao.astutils.rvcorrect and some procedures of the IDL Astrolib are used as well. Accuracy is about 0.5 seconds in time and about 1 m/s in velocity. History: written by Peter Mittermayer, Nov 8,2003 2005-January-13 Kudryavtsev Made more accurate calculation of the sidereal time. Conformity with MIDAS compute/barycorr is checked. 2005-June-20 Kochukhov Included precession of RA2000 and DEC2000 to current epoch """ from PyAstronomy.pyaC import degtorad # This reverts the original longitude convention. After this, # East longitudes are positive obs_long = -obs_long if jd < 2.4e6: PE.warn(PE.PyAValError("The given Julian Date (" + str(jd) + ") is exceedingly small. Did you subtract 2.4e6?")) # Covert JD to Gregorian calendar date xjd = jd year, month, day, ut = tuple(daycnv(xjd)) # Current epoch epoch = year + month/12. + day/365. # Precess ra2000 and dec2000 to current epoch, resulting ra is in degrees ra = ra2000 dec = dec2000 ra, dec = precess(ra, dec, 2000.0, epoch) # Calculate heliocentric julian date rjd = jd-2.4e6 hjd = helio_jd(rjd, ra, dec) + 2.4e6 # DIURNAL VELOCITY (see IRAF task noao.astutil.rvcorrect) # convert geodetic latitude into geocentric latitude to correct # for rotation of earth dlat = -(11.*60.+32.743)*np.sin(2.0*degtorad(obs_lat)) \ +1.1633*np.sin(4.0*degtorad(obs_lat)) - 0.0026*np.sin(6.0*degtorad(obs_lat)) lat = obs_lat + dlat/3600.0 # Calculate distance of observer from earth center r = 6378160.0 * (0.998327073+0.001676438*np.cos(2.0*degtorad(lat)) \ -0.00000351 * np.cos(4.0*degtorad(lat)) + 0.000000008*np.cos(6.0*degtorad(lat))) \ + obs_alt # Calculate rotational velocity (perpendicular to the radius vector) in km/s # 23.934469591229 is the sidereal day in hours for 1986 v = 2.*np.pi * (r/1000.) / (23.934469591229*3600.) # Calculating local mean sidereal time (see astronomical almanach) tu = (rjd-51545.0)/36525.0 gmst = 6.697374558 + ut + \ (236.555367908*(rjd-51545.0) + 0.093104*tu**2 - 6.2e-6*tu**3)/3600.0 lmst = idlMod(gmst-obs_long/15, 24) # Projection of rotational velocity along the line of sight vdiurnal = v*np.cos(degtorad(lat))*np.cos(degtorad(dec))*np.sin(degtorad(ra-lmst*15)) # BARICENTRIC and HELIOCENTRIC VELOCITIES vh, vb = baryvel(xjd,0) # Project to line of sight vbar = vb[0]*np.cos(degtorad(dec))*np.cos(degtorad(ra)) + vb[1]*np.cos(degtorad(dec))*np.sin(degtorad(ra)) + \ vb[2]*np.sin(degtorad(dec)) vhel = vh[0]*np.cos(degtorad(dec))*np.cos(degtorad(ra)) + vh[1]*np.cos(degtorad(dec))*np.sin(degtorad(ra)) + \ vh[2]*np.sin(degtorad(dec)) # Use barycentric velocity for correction corr = (vdiurnal + vbar) if debug: print '' print '----- HELCORR.PRO - DEBUG INFO - START ----' print '(obs_long (East positive),obs_lat,obs_alt) Observatory coordinates [deg,m]: ', -obs_long, obs_lat, obs_alt print '(ra,dec) Object coordinates (for epoch 2000.0) [deg]: ', ra,dec print '(ut) Universal time (middle of exposure) [hrs]: ', ut print '(jd) Julian date (middle of exposure) (JD): ', jd print '(hjd) Heliocentric Julian date (middle of exposure) (HJD): ', hjd print '(gmst) Greenwich mean sidereal time [hrs]: ', idlMod(gmst, 24) print '(lmst) Local mean sidereal time [hrs]: ', lmst print '(dlat) Latitude correction [deg]: ', dlat print '(lat) Geocentric latitude of observer [deg]: ', lat print '(r) Distance of observer from center of earth [m]: ', r print '(v) Rotational velocity of earth at the position of the observer [km/s]: ', v print '(vdiurnal) Projected earth rotation and earth-moon revolution [km/s]: ', vdiurnal print '(vbar) Barycentric velocity [km/s]: ', vbar print '(vhel) Heliocentric velocity [km/s]: ', vhel print '(corr) Vdiurnal+vbar [km/s]: ', corr print '----- HELCORR.PRO - DEBUG INFO - END -----' print '' return corr, hjd
def transitTimes(tmin, tmax, planetData, obsOffset=0., hjd=True, \ observatory=None, lon=None, lat=None, alt=None, minAltitude=None, \ showTwilight="all", moonDist=None, nexaInput=False, fileOutput=None): """ Calculate transit times for a given planet and a given period of time. The `planetData` dictionary must contain the following information: ======= ================================= Key Value ------- --------------------------------- ra Right ascension of object [deg] dec Declination of object [deg] T0 Time reference point (HJD) orbPer Orbital period [d] orbInc Orbital inclination [deg] SMA Semi-major axis [AU] RpJ Planetary radius [Jovian radii] RsSun Stellar Radius [solar] Tdur OPTIONAL, Transit duration [d] ======= ================================= If the transit duration (Tdur) is not given, the duration will be estimated using pyasl's `transitDuration` function. .. note:: The input times (`tmin` and `tmax`) are expected in JD (UT). Input time will be calculated to HJD. Time output is in HJD. Parameters ---------- tmin : float Start of time interval in Julian days (UT). tmax : float End of time interval in Julian days (UT). planetData: dictionary A dictionary containing the parameters of the exoplanet for which the transit times should be calculated. The required keys are specified above. obs_offset : float, optional Specifies additional time before AND after the transit in DAYS. This is useful if the observation should start and end some time before and after the actual transit. hjd : boolean, optional If True (default), the given Julian dates specifying the time interval (`tmin` and `tmax`) are automatically converted into the heliocentric frame (HJD). observatory : string, optional If given, pyasl's `observatory` function will be used to automatically resolve the name and obtain longitude, latitude, and altitude of the observatory. If `observatory` is given, `lon`, `lat`, and `alt` must not be specified. lon : float, optional East longitude of the observatory given in DEGREES. Longitude is positive in EASTWARD direction. If LON is not given, transitTimes will only return beginning and end of the observation and the time of mid transit. lat : float, optional Latitude of the observatory given in DEGREES (positive in NORTHWARD direction). alt : float, optional Altitude of the observatory given in METER. minAltitude : float, optional Minimum altitude of the object in DEGREES. If a minimum altitude is given, only transits for which the object is above the given altitude during the ENTIRE observation are included in the list created by `transitTimes`. Note that `minAltitude` can only be used if the observer's location has been specified either via `observatory` or via `lon`, `lat`, and `alt`. showTwilight : string, optional, {"all", "civil", "nautical", "astronomical", "night"} Specifies the twilight acceptable during the observation. By default all twilight conditions are acceptable. Only the transits for which the ENTIRE observation occurs during the specified or darker twilight conditions are listed. The choices are: - "all": all transits are shown (even during day) - "civil": only transits during civil twilight and better are shown - "nautical": only transits during nautical twilight and better are shown - "astronomical": only transits during astronomical twilight and better are shown - "night": only transits during night are shown Note that this can only have an effect, if the observer's location is specified. moonDist : float Minimum distance between the Moon and the target in DEGREES. By default all Moon distances are acceptable (moonDist=0.0). Only observations are listed for which the angular distance between the Moon and the target is larger than `moonDist` during the ENTIRE observation. Note that this can only have an effect, if the observer's location is specified. fileOutput : string or file, optional If a string is given, a file with the name will be created and the output will be written to that file. If a (writable) file object is given, the output will be written to that file. In both cases, no output will be given on screen. Returns ------- Transit times : dictionary Returns a dictionary containing the transit details. The dictionary key is a running number (starting with one), which is equivalent to that listed in the first column of the table. For each transit, the function returns a dictionary with the transit details. If the observer's location was not specified, the dictionary has the following keys: ============ ==================================================== Key Description ------------ ---------------------------------------------------- Planet name Name of the planet Tmid HJD of transit center Transit jd Array giving JD of start, mid-time, and end of transit. Obs jd Array specifying the HJD of the start, center and end of the observation. Obs cal Equivalent to 'Obs jd', but in the form of the calendar date. In particular, for each date, a list containing [Year, month, day, fractional hours] is given. **Below follows optional output only present** **if the observer's location is known** Obs coord East longitude [deg], latitude [deg], and altitude [m] of the observatory. Sun ra Right ascension of the Sun at center of observation. Sun dec Declination of the Sun at center of observation. Sun alt Altitude of the Sun [deg] at begin, center, and end of the observation. Sun az Azimuth if the Sun [deg] at begin, center, and end of the observation. Moon phase Array giving lunar phase (in percent) at start, center, and end of the observation. Moon AD Angular distance between the target and the Moon at begin, center, and end of the observation [deg]. Moon ra Right ascension of the Moon at begin, center, and end of the observation [deg]. Moon dec Declination of the Moon at begin, center, and end of the observation [deg]. Star ra Right ascension of the star [deg]. Star dec Declination of the star [deg]. Star CP Cardinal point of the star at begin, center, and end of the observation. Star alt Altitude of the star [deg] at begin, center, and end of the observation. Star az Azimuth of the star [deg] at begin, center, and end of the observation. Twilight The worst, i.e., brightest type of twilight encountered during the observation. ============ ==================================================== """ if fileOutput is not None: oldStdout = sys.stdout if isinstance(fileOutput, basestring): sys.stdout = open(fileOutput, 'w') else: sys.stdout = fileOutput try: if tmin >= tmax: raise(PE.PyAValError("The given time range is inconsistent (tmin >= tmax)", \ where="transitTimes", \ solution="Adapt tmin and tmax.")) # Copy input dictionary, because it may be changed planetData = planetData.copy() if nexaInput: pdin = planetData.copy() planetData = {} planetData["ra"] = pdin["ra"] planetData["dec"] = pdin["dec"] planetData["orbPer"] = pdin["pl_orbper"] planetData["T0"] = pdin["pl_tranmid"] planetData["orbInc"] = pdin["pl_orbincl"] planetData["SMA"] = pdin["pl_orbsmax"] planetData["RpJ"] = pdin["pl_radj"] planetData["RsSun"] = pdin["st_rad"] planetData["Tdur"] = pdin["pl_trandur"] planetData["plName"] = pdin["pl_name"] if np.isnan(planetData["Tdur"]): del planetData["Tdur"] # Check whether required keys are present reke = ["ra", "dec", "orbPer", "T0", "orbInc", "SMA", "RpJ", "RsSun", "plName"] msg = "" fail = False for key in reke: if (not key in planetData): msg += "The required key '" + key + "' is missing in the input data!\n" fail = True continue if isinstance(planetData[key], (int, long, float)): if np.isnan(planetData[key]): msg += "The required key '" + key + "' has NaN value in the input data\n!" fail = True if fail: raise(PE.PyAValError("The input `planetData` is inappropriate:\n" + msg, \ where="transitTimes", \ solution="Specify all required input values.")) # Object position [degrees] ra = planetData["ra"] dec = planetData["dec"] if hjd: # Convert input times into heliocentric frame tmin = helio_jd(tmin, ra, dec) tmax = helio_jd(tmax, ra, dec) print "Specified time span" print "Start date (DDDD-MM-YY and fractional hours): {0:4d}-{1:02d}-{2:02d} {3:6.3f}".format(*daycnv(tmin)) print "End date (DDDD-MM-YY and fractional hours): {0:4d}-{1:02d}-{2:02d} {3:6.3f}".format(*daycnv(tmax)) print # Transit parameters # Orbital period in days period = planetData["orbPer"] # Transit reference time (should be HJD) T0 = planetData["T0"] if not "Tdur" in planetData: # No duration specified in the data inc = planetData["orbInc"] # deg sma = planetData["SMA"] # au rp = planetData["RpJ"] # Mjup rs = planetData["RsSun"] # Msun dur = transitDuration(sma, rp, rs, inc, period) print "Estimating transit duration using orbital inclination, semi-major axis," print " planetary radius, and stellar radius" else: dur = planetData["Tdur"] print "Transit duration: ", dur*24.*60., " minutes" print "Off-transit time before and after transit: ", obsOffset*24.*60., " minutes" # First and last epoch contained in specified range trnum_start = np.floor( (tmin - T0)/period ) trnum_end = np.ceil( (tmax - T0)/period ) # Relevant transit epochs tr = np.arange( trnum_start, trnum_end, 1) if (observatory is not None) and \ ((lon is not None) or (lat is not None) or (alt is not None)): raise(PE.PyAParameterConflict("You must either specify `observatory` OR `lon`, `lat`, and `alt`.", \ where="transitTimes", \ solution="Adapt function call.")) if observatory is not None: # Resolve observatory string observatory_data = pyaobs.observatory(observatory) lon = observatory_data["longitude"] lat = observatory_data["latitude"] alt = observatory_data["altitude"] # Check if the observatory data are complete obsCompl = (lon is None) + (lat is None) + (alt is None) if (obsCompl == 1) or (obsCompl == 2): raise(PE.PyAValError("Observatory data is incomplete. `lon`, `lat`, and `alt` must all be specified.\n" + \ "Current values are: lon = " + str(lon) + ", lat = " + str(lat) + ", alt = " + str(alt), \ where="transitTimes", \ solution="Provide complete observatory information.")) if (minAltitude is not None) and (lon is None): # Observer's location not given so minAltitude cannot have any effect raise(PE.PyAParameterConflict("The observer's location is not specified, but `minAltitude` is given.\n" + \ "This parameter can only be used, if the observer's location is known.", \ where="transitTimes", \ solution="Either specify the observer's location or set `minAltitude` to None.")) if (showTwilight != "all") and (lon is None): # Observer's location not given so showTwilight cannot have any effect raise(PE.PyAParameterConflict("The observer's location is not specified, but `showTwilight` is given.\n" + \ "This parameter can only be used, if the observer's location is known.", \ where="transitTimes", \ solution="Either specify the observer's location or set `showTwilight` to \"all\".")) if (showTwilight != "all") and (showTwilight != "civil") and (showTwilight != "nautical") and \ (showTwilight != "astronomical") and (showTwilight != "night"): # None of the possible choices for showTwilight have been used. raise(PE.PyAValError("Wrong keyword given for showTwilight.\n" + \ "Current keyword is " + showTwilight, \ where="transitTimes", \ solution="Select a valid keyword for showTwilight: `all', `civil', `nautical', `astronomical', or `night'.")) if moonDist is None: # No limit on Moon distance moonDist = 0.0 if (moonDist != 0.0) and (lon is None): # Observer's location not given so showTwilight cannot have any effect raise(PE.PyAParameterConflict("The observer's location is not specified, but `moonDist` is given.\n" + \ "This parameter can only be used, if the observer's location is known.", \ where="transitTimes", \ solution="Either specify the observer's location or set `moonDist` to 0.0 or None.")) if moonDist < 0.0: # Moon distance below zero does not make sense PE.warn("The specified `moonDist' is below zero ("+str(moonDist)+") which does not make sense.\n"+\ "It was changed to 0.0.\n"+\ "Please use a value >= 0.0 or None if specifying `moonDist'.") print if np.logical_and(lon != None, lat != None): print "No. Tmid [HJD] Obs. start [UT] [ALT, DIR(AZI)] Transit mid [UT] [ALT, DIR(AZI)] Obs. end [UT] [ALT, DIR(AZI)] twilight"+\ " (SUN ALT) moon distance moon phase" else: print "No. Tmid [HJD] Obs. start [UT] Transit mid [UT] Obs. end [UT]" allData = {} trcounter = 1 for i in tr: trData = {} # Get times Tmid = T0 + float(i)*period obs_start_hjd = Tmid - (dur/2.0) - obsOffset obs_start = daycnv(obs_start_hjd) obs_mid = daycnv(Tmid) obs_end_hjd = Tmid + (dur/2.0) + obsOffset obs_end = daycnv(obs_end_hjd) time_temp = np.array([obs_start_hjd, Tmid, obs_end_hjd]) transit_only = np.array([Tmid - (dur/2.0), Tmid, Tmid + (dur/2.0)]) # Get visibility if (lon is not None) and (lat is not None): # Get alt/az of object for current transit altaz = eq2hor.eq2hor(time_temp, np.ones(time_temp.size)*ra, \ np.ones(time_temp.size)*dec, lon=lon, lat=lat, alt=alt) # If minimum altitude is not fulfilled during observation, # do not show transit if minAltitude is not None: minalt = np.where(altaz[0] >= minAltitude)[0] if len(minalt) < time_temp.size: # Skip this transit continue # Get Sun position for current transit sunpos_radec = sunpos.sunpos(time_temp[1]) sunpos_altaz = eq2hor.eq2hor(time_temp, np.ones(time_temp.size)*sunpos_radec[1], \ np.ones(time_temp.size)*sunpos_radec[2], \ lon=lon, lat=lat, alt=alt) twi = twilight.twilightName(max(sunpos_altaz[0])) # Check type of twilight -> if requirement not fulfilled, don't show transit if showTwilight == "civil": # Show civil or better if twi == "day": continue if showTwilight == "nautical": # Show nautical or better if twi == "day": continue if twi == "civil twilight": continue if showTwilight == "astronomical": # Show astronomical or better if twi == "day": continue if twi == "civil twilight": continue if twi == "nautical twilight": continue if showTwilight == "night": # Only show night if twi != "night": continue # Get Moon position for current transit mpos = moonpos(time_temp) mdists = [] for i in range(time_temp.size): mdists.append(getAngDist(mpos[0][i], mpos[1][i], ra, dec)) mdist = min(mdists) # Check Moon distance, if not fulfilled, neglect the transit if mdist < moonDist: continue # Get lunar phase in percent moonpha = moonphase(time_temp) * 100. print "%3d %10.5f %2d.%2d. %2d:%02d [%3d°,%s(%3d°)] %2d.%2d. %2d:%02d [%3d°,%s(%3d°)] %2d.%2d. %2d:%02d [%3d°,%s(%3d°)] %18s (%3d°,%3d°,%3d°) (%3d°,%3d°,%3d°) %3d%%" \ %(trcounter, Tmid, obs_start[2], obs_start[1], np.floor(obs_start[3]), (obs_start[3]-np.floor(obs_start[3]))*60., \ altaz[0][0], getCardinalPoint(altaz[1][0]), altaz[1][0], \ obs_mid[2], obs_mid[1], np.floor(obs_mid[3]), (obs_mid[3]-np.floor(obs_mid[3]))*60., \ altaz[0][1], getCardinalPoint(altaz[1][1]), altaz[1][1], \ obs_end[2], obs_end[1], np.floor(obs_end[3]), (obs_end[3]-np.floor(obs_end[3]))*60., \ altaz[0][2], getCardinalPoint(altaz[1][2]), altaz[1][2], twi, sunpos_altaz[0][0], sunpos_altaz[0][1], sunpos_altaz[0][2], \ mdists[0], mdists[1], mdists[2], np.max(moonpha)) # Save transit data trData["Tmid"] = Tmid trData["Obs jd"] = time_temp trData["Obs cal"] = [obs_start, obs_mid, obs_end] trData["Star ra"] = ra trData["Star dec"] = dec trData["Star alt"] = altaz[0] trData["Star az"] = altaz[1] trData["Sun ra"] = sunpos_radec[1] trData["Sun dec"] = sunpos_radec[2] trData["Sun alt"] = sunpos_altaz[0] trData["Sun az"] = sunpos_altaz[1] trData["Twilight"] = twi trData["Moon ra"] = mpos[0] trData["Moon dec"] = mpos[1] trData["Moon AD"] = mdist trData["Moon phase"] = moonpha trData["Star CP"] = [getCardinalPoint(altaz[1][0]), getCardinalPoint(altaz[1][1]), getCardinalPoint(altaz[1][2])] trData["Obs coord"] = [lon, lat, alt] else: # If you do not specify the observer's location, return all transits of the object print "%3d %10.5f %2d.%2d. %2d:%02d %2d.%2d. %2d:%02d %2d.%2d. %2d:%02d" \ %(trcounter, Tmid, obs_start[2], obs_start[1], np.floor(obs_start[3]), (obs_start[3]-np.floor(obs_start[3]))*60., \ obs_mid[2], obs_mid[1], np.floor(obs_mid[3]), (obs_mid[3]-np.floor(obs_mid[3]))*60., \ obs_end[2], obs_end[1], np.floor(obs_end[3]), (obs_end[3]-np.floor(obs_end[3]))*60. ) trData["Tmid"] = Tmid trData["Obs jd"] = time_temp trData["Obs cal"] = [obs_start, obs_mid, obs_end] trData["Transit jd"] = transit_only trData["Planet name"] = planetData["plName"] allData[trcounter] = trData trcounter += 1 if len(allData.keys()) == 0: print print "------------------------------------------------------" print "!!! No transits found for the given restrictions. !!!" print "------------------------------------------------------" print except: raise finally: if fileOutput is not None: if isinstance(fileOutput, basestring): sys.stdout.close() sys.stdout = oldStdout return allData