def derivative(x): """ This computes the simple numerical derivative of x by convolving with kernel [-1,0,1]. Parameters ---------- x : list, np.ndarray The array from which the derivative is required. Returns ------- derivative : np.array The numerical derivative of x. Example ------- >>> import numpy as np >>> x=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] >>> dx=derivative(x) """ import numpy as np from tayph.vartests import typetest, dimtest typetest(x, [list, np.ndarray], 'x in derivative()') dimtest(x, [0], 'x in derivative()') x = np.array(x) d_kernel = np.array([-1, 0, 1]) / 2.0 return (convolve(x, d_kernel, fit_width=3))
def end(start, id='', silent=False): """ Short-hand for ending a timing measurement and printing the elapsed time. Parameters ---------- start : float Generated by time.time() id : str Description or numeral to identify the clock associated with the start time. Returns ------- elapsed : float The time elapsed since start. """ from tayph.vartests import typetest typetest(start, float, 'start time in utils.end()') typetest(id, str, 'id/descriptor in utils.end()') import time end = time.time() if not silent: print('Elapsed %s: %s' % ('on timer ' + id, end - start)) return end - start
def dRV(dp): """This program calculates the change in radial velocity in km/s for the planet in the data sequence provided in dp, the data-path. dp starts in the root folder,i.e. it starts with data/projectname/, and it ends with a slash. Example: dv=dRV('data/Kelt-9/night1/') The output is an array with length N, corresponding to N exposures. The change in radial velocity is calculated using the first derivative of the formula for RV, multiplied by the exposure time provided in obs_times. The answer is provided in units of km/s change within each exposure.""" from tayph.vartests import typetest import numpy as np import astropy.units as u from astropy.io import ascii import pdb import tayph.util as ut dp=ut.check_path(dp,exists=True) obsp=ut.check_path(dp/'obs_times',exists=True) d=ascii.read(obsp,comment="#") #Texp=d['exptime'].astype('float') Texp=d['col3'].data#astype('float') vorb=v_orb(dp) p=phase(dp) P=paramget('P',dp) i=paramget('inclination',dp) typetest(P,float,'P in dRV()') typetest(i,float,'i in dRV()') dRV=vorb*np.cos(2.0*np.pi*p)*2.0*np.pi/((P*u.d).to('s').value)*np.sin(np.radians(i)) return abs(dRV*Texp)
def t_eff(M,R): """This function computes the mass and radius of a star given its mass and radius relative to solar.""" from tayph.vartests import typetest import numpy as np import astropy.constants as const typetest(M,[int,float],'M in t_eff()') typetest(R,[int,float],'R in t_eff()') M=float(M) R=float(R) Ms = const.M_sun Rs = const.R_sun Ls = const.L_sun sb = const.sigma_sb if M < 0.43: a = 0.23 b = 2.3 elif M < 2: a = 1.0 b = 4.0 elif M < 55: a = 1.4 b = 3.5 else: a = 32000.0 b = 1.0 T4 = a*M**b * Ls / (4*np.pi*R**2*Rs**2*sb) return(T4**0.25)
def airtovac(wlnm): """ This converts air wavelengths to vaccuum wavelengths. Parameters ---------- wlnm : float, np.ndarray The wavelength that is to be converted. Returns ------- wlnm : float, np.array wavelengths in vaccuum. Example ------- >>> import numpy as np >>> wlA=np.array([500.0,510.0,600.0,700.0]) >>> wlV=airtovac(wlA) """ import numpy as np from tayph.vartests import typetest typetest(wlnm, [float, np.ndarray], 'wlmn in airtovac()') wlA = wlnm * 10.0 s = 1e4 / wlA n = 1 + 0.00008336624212083 + 0.02408926869968 / ( 130.1065924522 - s**2) + 0.0001599740894897 / (38.92568793293 - s**2) return (wlA * n / 10.0)
def load_columns_from_file(dp,maskname,mode='strict'): """ This loads the list of lists of columns back into memory after having been saved by a call of write_columns_to_file() below. """ import tayph.util as ut from tayph.vartests import typetest import pickle import os from pathlib import Path ut.check_path(dp) typetest(maskname,str,'maskname in load_columns_from_file()') typetest(mode,str,'mode in load_columns_from_file()') outpath=Path(dp)/(maskname+'_columns.pkl') if os.path.isfile(outpath) == False: if mode == 'strict': raise Exception('FileNotFoundError in reading columns from file: Column file named %s do not exist at %s.' % (maskname,dp)) else: print('---No previously saved manual mask exists. User will start a new mask.') return([]) else: print(f'------Loading previously saved manual mask {str(outpath)}.') pickle_in = open(outpath,"rb") return(pickle.load(pickle_in))
def vactoair(wlnm): """ This converts vaccuum wavelengths to air wavelengths. Parameters ---------- wlnm : float, np.ndarray The wavelength that is to be converted. Returns ------- wlnm : float, np.array wavelengths in air. Example ------- >>> import numpy as np >>> wlV=np.array([500.0,510.0,600.0,700.0]) >>> wlA=vactoair(wlV) """ import numpy as np from tayph.vartests import typetest typetest(wlnm, [float, np.ndarray], 'wlmn in vactoair()') wlA = wlnm * 10.0 s = 1e4 / wlA f = 1.0 + 5.792105e-2 / (238.0185e0 - s**2) + 1.67917e-3 / (57.362e0 - s**2) return (wlA / f / 10.0)
def running_MAD_2D(z, w, verbose=False, parallel=False): """Computers a running standard deviation of a 2-dimensional array z. The stddev is evaluated over the vertical block with width w pixels. The output is a 1D array with length equal to the width of z. This is very slow on arrays that are wide in x (hundreds of thousands of points).""" import astropy.stats as stats import numpy as np from tayph.vartests import typetest, dimtest, postest import tayph.util as ut if parallel: from joblib import Parallel, delayed typetest(z, np.ndarray, 'z in fun.running_MAD_2D()') dimtest(z, [0, 0], 'z in fun.running_MAD_2D()') typetest(w, [int, float], 'w in fun.running_MAD_2D()') postest(w, 'w in fun.running_MAD_2D()') size = np.shape(z) ny = size[0] nx = size[1] s = np.arange(0, nx, dtype=float) * 0.0 dx1 = int(0.5 * w) dx2 = int(int(0.5 * w) + (w % 2)) #To deal with odd windows. for i in range(nx): minx = max([0, i - dx1]) #This here is only a 3% slowdown. maxx = min([nx, i + dx2]) s[i] = stats.mad_std( z[:, minx:maxx], ignore_nan=True) #This is what takes 97% of the time. if verbose: ut.statusbar(i, nx) return (s)
def astropyberv(dp): """ This does the same as berv(dp), but uses astropy to compute the BERV for the dates of observation given a data parameter file. Useful if the BERV keyword was somehow wrong or missing, or if you wish to cross-validate. Requires latitude, longitude, ra, dec and elevation to be provided in the config file as lat, long, RA, DEC and elev in units of degrees and meters. Date should be provided as mjd. """ from tayph.vartests import typetest from pathlib import Path import numpy as np from astropy.io import ascii from astropy.time import Time from astropy import units as u from astropy.coordinates import SkyCoord, EarthLocation dp=check_dp(dp)#Path object d=ascii.read(dp/'obs_times',comment="#")#,names=['mjd','time','exptime','airmass']) #Not using named columns because I may not know for sure how many columns #there are, and read-ascii breaks if only some columns are named. #The second column has to be an MJD date array though. dates = d['col1'] RA=paramget('RA',dp) DEC=paramget('DEC',dp) typetest(RA,str,'RA in sp.astropyberv()') typetest(DEC,str,'DEC in sp.astropyberv()') berv = [] observatory = EarthLocation.from_geodetic(lat=paramget('lat',dp)*u.deg, lon=paramget('long',dp)*u.deg, height=paramget('elev',dp)*u.m) sc = SkyCoord(RA+' '+DEC, unit=(u.hourangle, u.deg)) for date in dates: barycorr = sc.radial_velocity_correction(obstime=Time(date,format='mjd'), location=observatory).to(u.km/u.s) berv.append(barycorr.value) return berv
def envelope(wlm, fxm, binsize, selfrac=0.05, mode='top', threshold=''): """ This program measures the top or bottom envelope of a spectrum (wl,fx), by chopping it up into bins of size binsze (unit of wl), and measuring the mean of the top n % of values in that bin. Setting the mode to 'bottom' will do the oppiste: The mean of the bottom n% of values. The output is the resulting wl and flux points of these bins. Example: wle,fxe = envelope(wl,fx,1.0,selfrac=3.0,mode='top') """ import numpy as np import tayph.util as ut import tayph.functions as fun from tayph.vartests import typetest, dimtest, nantest, postest from matplotlib import pyplot as plt typetest(wlm, np.ndarray, 'wlm in ops.envelope()') typetest(fxm, np.ndarray, 'fxm in ops.envelope()') dimtest(wlm, [len(fxm)]) typetest(binsize, [int, float], 'binsize in ops.envelope()') typetest(selfrac, float, 'percentage in ops.envelope()') typetest(mode, str, 'mode in envelope') nantest(fxm, 'fxm in ops.envelope()') nantest(wlm, 'wlm in ops.envelope()') postest(wlm, 'wlm in ops.envelope()') postest(binsize, 'binsize in ops.envelope()') if mode not in ['top', 'bottom']: raise Exception( f'RuntimeError in ops.envelope(): Mode should be set to "top" or "bottom" ({mode}).' ) binsize = float(binsize) if mode == 'bottom': fxm *= -1.0 wlcs = np.array([]) #Center wavelengths fxcs = np.array([]) #Flux at center wavelengths i_start = 0 wlm_start = wlm[i_start] for i in range(0, len(wlm) - 1): if wlm[i] - wlm_start >= binsize: wlsel = wlm[i_start:i] fxsel = fxm[i_start:i] maxsel = fun.selmax(fxsel, selfrac) wlcs = np.append(wlcs, np.mean(wlsel[maxsel])) fxcs = np.append(fxcs, np.mean(fxsel[maxsel])) i_start = i + 1 wlm_start = wlm[i + 1] if isinstance(threshold, float) == True: #This means that the threshold value is set, and we set all bins less than #that threshold to the threshold value: if mode == 'bottom': threshold *= -1.0 fxcs[(fxcs < threshold)] = threshold if mode == 'bottom': fxcs *= -1.0 fxm *= -1.0 return wlcs, fxcs
def set_order(self,i): """ This modifies the currently active order to be plotted. """ import numpy as np import tayph.functions as fun import warnings import tayph.util as ut import copy from tayph.vartests import typetest typetest(i,int,'i in mask_maker.set_order()',) self.wl = self.list_of_wls[i] self.order = self.list_of_orders[i] #Measure the shape of the current order self.nexp = np.shape(self.order)[0] self.npx = np.shape(self.order)[1] #Compute the meanspec and the residuals, ignoring runtime warnings related to NaNs: with warnings.catch_warnings(): warnings.simplefilter("ignore", category=RuntimeWarning) self.meanspec = np.nanmean(self.list_of_orders[i],axis=0) self.residual = self.order / self.meanspec self.img_max = np.nanmean(self.meanspec[fun.selmax(self.meanspec,0.02,s=0.02)])*1.3 self.vmin = np.nanmedian(self.residual)-3.0*np.nanstd(self.residual) self.vmax = np.nanmedian(self.residual)+3.0*np.nanstd(self.residual)
def get_model(name, library='models/library', root='models'): """This program queries a model from a library file, with predefined models for use in model injection, cross-correlation and plotting. These models have a standard format. They are 2 rows by N points, where N corresponds to the number of wavelength samples. The first row contains the wavelengths, the second the associated flux values. The library file has two columns that are formatted as follows: modelname modelpath modelname modelpath modelname modelpath modelpath starts in the models/ subdirectory. Set the root variable if a location is required other than the ./models directory. Example call: wlm,fxm = get_model('WASP-121_TiO',library='models/testlib') """ from tayph.vartests import typetest, dimtest from tayph.util import check_path from astropy.io import fits from pathlib import Path import errno import os typetest(name, str, 'name in mod.get_model()') library = check_path(library, exists=True) #First open the library file. f = open(library, 'r') x = f.read().splitlines() #Read everything into a big string array. f.close() n_lines = len(x) #Number of models in the library. models = {} #This will contain the model names. for i in range(0, n_lines): line = x[i].split() value = (line[1]) models[line[0]] = value try: modelpath = Path(models[name]) except KeyError: raise KeyError( f'Model {name} is not present in library at {str(library)}' ) from None if str(modelpath)[0] != '/': #Test if this is an absolute path. root = check_path(root, exists=True) modelpath = root / modelpath try: modelarray = fits.getdata( modelpath ) #Two-dimensional array with wl on the first row and flux on the second row. except FileNotFoundError: raise FileNotFoundError( f'Model file {modelpath} from library {str(library)} does not exist.' ) dimtest(modelarray, [2, 0]) return (modelarray[0, :], modelarray[1, :])
def apply_mask_from_file(dp,maskname,list_of_orders): import astropy.io.fits as fits import numpy as np import os.path import sys import tayph.util as ut from tayph.vartests import typetest,dimtest from pathlib import Path import pickle ut.check_path(dp) typetest(maskname,str,'maskname in write_mask_to_file()') typetest(list_of_orders,list,'list_of_orders in apply_mask_from_file()') N = len(list_of_orders) inpath_auto = Path(dp)/(maskname+'_auto.pkl') inpath_man = Path(dp)/(maskname+'_manual.pkl') if os.path.isfile(inpath_auto) == False and os.path.isfile(inpath_man) == False: raise Exception(f'FileNotFoundError in apply_mask_from_file: Both mask files named ' f'{maskname} do not exist at {str(dp)}. Rerun with make_maske = True.') #At this point either of the mask files is determined to exist. #Apply the masks to the orders, by adding. This works because the mask is zero #everywhere, apart from the NaNs, and x+0=x, while x+NaN = NaN. if os.path.isfile(inpath_auto) == True: print(f'------Applying sigma_clipped mask from {inpath_auto}') # cube_of_masks_auto = fits.getdata(inpath_auto) with open(inpath_auto,"rb") as f: list_of_masks_auto = pickle.load(f) Nm = len(list_of_masks_auto) err = f'ERROR in apply_mask_from_file: List_of_orders and list_of_masks_auto do not have ' f'the same length ({N} vs {Nm}), meaning that the number of orders provided and the number ' 'of orders onto which the masks were created are not the same. This could have happened if ' 'you copy-pased mask_auto from one dataset to another. This is not recommended anyway, as ' 'bad pixels / outliers are expected to be in different locations in different datasets.' if Nm != N: raise Exception(err) #Checks have passed. Add the mask to the list of orders. for i in range(N): list_of_orders[i]+=list_of_masks_auto[i] if os.path.isfile(inpath_man) == True: print(f'------Applying manually defined mask from {inpath_man}') # cube_of_masks_man = fits.getdata(inpath_man) with open(inpath_man,"rb") as f: list_of_masks_man = pickle.load(f) Nm = len(list_of_masks_man) err = f'ERROR in apply_mask_from_file: List_of_orders and list_of_masks_auto do not have the same length ({N} vs {Nm}), meaning that the number of orders provided and the number of orders onto which the masks were created are not the same. This could have happened if you copy-pased mask_auto from one dataset to another. This is not recommended anyway, as bad pixels / outliers are expected to be in different locations in different datasets.' if Nm != N: raise Exception(err) for i in range(N): list_of_orders[i]+=list_of_masks_man[i] return(list_of_orders)
def running_mean_2D(D, w): """This computes a running mean on a 2D array in a window with width w that slides over the array in the horizontal (x) direction.""" import numpy as np from tayph.vartests import typetest, dimtest, postest typetest(D, np.ndarray, 'z in fun.running_mean_2D()') typetest(w, [int, float], 'w in fun.running_mean_2D()') postest(w, 'w in fun.running_mean_2D()') ny, nx = D.shape m2 = strided_window(D, w, pad=True) s = np.nanmean(m2, axis=(1, 2)) return (s)
def set_spectrum(self, i): """This modifies the currently active spectrum to be plotted.""" import numpy as np import tayph.functions as fun from tayph.vartests import typetest import matplotlib.pyplot as plt typetest(i, int, 'i in molecfit_gui/set_order') self.wl = self.wls[i] self.spectrum = self.fxc[i] self.Tspectrum = self.trans[i] self.img_max = np.nanmean(self.spectrum[fun.selmax( self.spectrum, 0.02, s=0.02)]) * 1.3
def findgen(n,integer=False): """This is basically IDL's findgen function. a = findgen(5) will return an array with 5 elements from 0 to 4: [0,1,2,3,4] """ import numpy as np from tayph.vartests import typetest,postest typetest(n,[int,float],'n in findgen()') typetest(integer,bool,'integer in findgen()') postest(n,'n in findgen()') n=int(n) if integer: return np.linspace(0,n-1,n).astype(int) else: return np.linspace(0,n-1,n)
def write_columns_to_file(dp,maskname,list_of_selected_columns): """ This dumps the list of list of columns that are manually selected by the user to a pkl file for loading at a later time. This is done to allow the user to resume work on a saved mask. """ import pickle from pathlib import Path import tayph.util as ut from tayph.vartests import typetest ut.check_path(dp) typetest(maskname,str,'maskname in write_columns_to_file()') typetest(list_of_selected_columns,list,'list_of_selected_columns in write_columns_to_file()') outpath=Path(dp)/(maskname+'_columns.pkl') print(f'---Saving list of masked columns to {str(outpath)}') with open(outpath, 'wb') as f: pickle.dump(list_of_selected_columns, f)
def phase(dp): """ Calculates the orbital phase of the planet in the data sequence provided using the parameters in dp/config and the timings in dp/obstimes. The output is an array with length N, corresponding to N exposures. Be CAREFUL: This program provides a time difference of ~1 minute compared to IDL/calctimes. This likely has to do with the difference between HJD and BJD, and the TDB timescale. In the future you should have a thorough look at the time-issue, because you should be able to get this right to the second. At the very least, make sure that the time conventions are ok. More importantly: The transit center time needs to be provided in config in BJD. """ from tayph.vartests import typetest import numpy as np from astropy.io import ascii from astropy.time import Time from astropy import units as u, coordinates as coord import tayph.util as ut dp=check_dp(dp)#Path object d=ascii.read(dp/'obs_times',comment="#")#,names=['mjd','time','exptime','airmass']) #Not using the named columns because I may not know for sure how many columns #there are, and read-ascii breaks if only some columns are named. #The second column has to be a date array though. # t = Time(d['col2'],scale='utc', location=coord.EarthLocation.of_site('paranal'))# I determined that the difference between this and geodetic 0,0,0 is zero. t = Time(d['col2'],scale='utc', location=coord.EarthLocation.from_geodetic(0,0,0)) jd = t.jd P=paramget('P',dp) RA=paramget('RA',dp) DEC=paramget('DEC',dp) Tc=paramget('Tc',dp)#Needs to be given in BJD! typetest(P,float,'P in sp.phase()') typetest(Tc,float,'Tc in sp.phase()') typetest(RA,str,'RA in sp.phase()') typetest(DEC,str,'DEC in sp.phase()') ip_peg = coord.SkyCoord(RA,DEC,unit=(u.hourangle, u.deg), frame='icrs') ltt_bary = t.light_travel_time(ip_peg) n=0.0 Tc_n=Time(Tc,format='jd',scale='tdb') while Tc_n.jd >= min(jd): Tc_n=Time(Tc-100.0*n*P,format='jd',scale='tdb')#This is to make sure that the Transit central time PRECEDES the observations (by tens or hundreds or thousands of years). Otherwise, the phase could pick up a minus sign somewhere and be flipped. I wish to avoid that. n+=1 BJD = t.tdb + ltt_bary diff = BJD-Tc_n phase=((diff.jd) % P)/P return phase
def RV_star(dp): """ This calculates the radial velocity in km/s for the star in the data sequence provided in dp. The output is an array with length N, corresponding to N exposures. The radial velocity is provided in km/s. This is meant to be used to correct (align) the stellar spectra to the same reference frame. It requires K (the RV-semi amplitude to be provided in the config file, in km/s as well. Often this value is given in discovery papers. Like all my routines, this assumes a circular orbit. """ from tayph.vartests import typetest import numpy as np dp=check_dp(dp) p=phase(dp) K=paramget('K',dp) typetest(K,float,'K in sp.RV_star()') rv=K*np.sin(2.0*np.pi*p) * (-1.0) return(rv)
def running_MAD(z,w): """Computers a running standard deviation of a 1-dimensional array z. The stddev is evaluated over a range with width w pixels. The output is a 1D array with length equal to the width of z.""" import astropy.stats as stats import numpy as np from tayph.vartests import typetest,dimtest,postest typetest(z,np.ndarray,'z in fun.running_MAD()') typetest(w,[int,float],'w in fun.running_MAD()') postest(w,'w in fun.running_MAD_2D()') nx = len(z) s = np.arange(0,nx,dtype=float)*0.0 dx1=int(0.5*w) dx2=int(int(0.5*w)+(w%2))#To deal with odd windows. for i in range(nx): minx = max([0,i-dx1]) maxx = min([nx,i+dx2]) s[i] = stats.mad_std(z[minx:maxx],ignore_nan=True) return(s)
def running_MAD_2D(z,w): """Computers a running standard deviation of a 2-dimensional array z. The stddev is evaluated over the vertical block with width w pixels. The output is a 1D array with length equal to the width of z.""" import astropy.stats as stats import numpy as np from tayph.vartests import typetest,dimtest,postest typetest(z,np.ndarray,'z in fun.running_MAD_2D()') dimtest(z,[0,0],'z in fun.running_MAD_2D()') typetest(w,[int,float],'w in fun.running_MAD_2D()') postest(w,'w in fun.running_MAD_2D()') size = np.shape(z) ny = size[0] nx = size[1] s = findgen(nx)*0.0 for i in range(nx): minx = max([0,i-int(0.5*w)]) maxx = min([nx-1,i+int(0.5*w)]) s[i] = stats.mad_std(z[:,minx:maxx],ignore_nan=True) return(s)
def sigma_clip(array,nsigma=3.0,MAD=False): """This returns the n-sigma boundaries of an array, mainly used for scaling plots. Parameters ---------- array : list, np.ndarray The array from which the n-sigma boundaries are required. nsigma : int, float The number of sigma's away from the mean that need to be provided. MAD : bool Use the true standard deviation or MAD estimator of the standard deviation (works better in the presence of outliers). Returns ------- vmin,vmax : float The bottom and top n-sigma boundaries of the input array. """ from tayph.vartests import typetest import numpy as np typetest(array,[list,np.ndarray],'array in fun.sigma_clip()') typetest(nsigma,[int,float],'nsigma in fun.sigma_clip()') typetest(MAD,bool,'MAD in fun.sigma_clip()') m = np.nanmedian(array) if MAD: from astropy.stats import mad_std s = mad_std(array,ignore_nan=True) else: s = np.nanstd(array) vmin = m-nsigma*s vmax = m+nsigma*s return vmin,vmax
def write_mask_to_file(dp,maskname,list_of_masks_auto,list_of_masks_manual=[]): import sys from pathlib import Path import pickle import tayph.util as ut from tayph.vartests import typetest,dimtest,lentest import pdb import numpy as np ut.check_path(dp) typetest(maskname,str,'maskname in write_mask_to_file()') typetest(list_of_masks_auto,list,'list_of_masks_auto in write_mask_to_file()') typetest(list_of_masks_manual,list,'list_of_masks_manual in write_mask_to_file()') lentest(list_of_masks_auto,len(list_of_masks_manual),'list_of_masks_auto in ' 'write_mask_to_file()') for i in range(len(list_of_masks_auto)): dimtest(list_of_masks_auto[i],np.shape(list_of_masks_manual[i]),'list_of_masks_auto in ' 'write_mask_to_file()') outpath=Path(dp)/maskname if len(list_of_masks_auto) == 0 and len(list_of_masks_manual) == 0: print('RuntimeError in write_mask_to_file: Both lists of masks are emtpy!') sys.exit() print(f'---Saving lists of auto and manual masks to {str(outpath)}') if len(list_of_masks_auto) > 0: with open(str(outpath)+'_auto.pkl', 'wb') as f: pickle.dump(list_of_masks_auto,f) # ut.save_stack(str(outpath)+'_auto.fits',list_of_masks_auto) if len(list_of_masks_manual) > 0: with open(str(outpath)+'_manual.pkl', 'wb') as f: pickle.dump(list_of_masks_manual,f)
def save_stack(filename, list_of_2D_frames): """This code saves a stack of fits-files to a 3D cube, that you can play through in DS9. For diagnostic purposes. Parameters ---------- filename : str, Path Output filename/path. list_of_2D-frames : list A list with 2D arrays Returns ------- elapsed : float The time elapsed since start. """ import astropy.io.fits as fits import numpy as np import pathlib from tayph.vartests import typetest from tayph.vartests import dimtest import warnings filename = check_path(filename, 'filename in save_stack()') typetest(list_of_2D_frames, list, 'list_of_2D_frames in save_stack()') #Test that its a list typetest(list_of_2D_frames[0], [list, np.ndarray], 'list_of_2D_frames in save_stack()') for i, f in enumerate(list_of_2D_frames): typetest(f, [list, np.ndarray], 'frame %s of list_of_2D_frames in save_stack()' % i) base = np.shape(list_of_2D_frames[0]) N = len(list_of_2D_frames) dimtest(base, [2], 'shape of list_of_2D_frames in save_stack()' ) #Test that its 2-dimensional for i, f in enumerate(list_of_2D_frames): dimtest(f, base, varname='frame %s of list_of_2D_frames in save_stack()' % i) #Test that all have the same shape. N = len(list_of_2D_frames) if N > 0: out = np.zeros((base[0], base[1], N)) for i in range(N): out[:, :, i] = list_of_2D_frames[i] fits.writeto(filename, np.swapaxes(np.swapaxes(out, 2, 0), 1, 2), overwrite=True) else: warnings.warn( "List_of_2D_frames has length zero. No output was generated by save_stack().", RuntimeWarning)
def RV(dp,vorb=None,vsys=False): """This program calculates the radial velocity in km/s for the planet in the data sequence provided in dp, the data-path. dp starts in the root folder, i.e. it starts with data/projectname/, and it ends with a slash. Example: v=RV('data/Kelt-9/night1/') The output is an array with length N, corresponding to N exposures. The radial velocity is provided in km/s.""" import tayph.util as ut import numpy as np from tayph.vartests import typetest dp=ut.check_path(dp) p=phase(dp) i=paramget('inclination',dp) typetest(i,float,'i') if vorb == None: vorb=v_orb(dp) typetest(vorb,float,'vorb in sp.RV') rv=vorb*np.sin(2.0*np.pi*p)*np.sin(np.radians(i)) if vsys == True: vs=paramget('vsys',dp) rv+=vs return rv#In km/s.
def bin(x, y, n, err=[]): """ A simple function to quickly bin a spectrum by a certain number of points. Parameters ---------- x : list, np.ndarray The horizontal axis. y : list, np.ndarray The array corresponding to x. n : int, float The number of points by which to bin. Int is recommended, and it is converted to int if a float is provided, with the consequence of the rounding that int() does. err : list, np.array, optional If set, errors corresponding to the flux points. Returns ------- x_bin : np.array The binned x-axis. y_bin : np.array The binned y-axis. e_bin : np.array Only if err is set to an array with non-zero length. """ from tayph.vartests import typetest, dimtest, minlength import numpy as np typetest(x, [list, np.ndarray], 'x in ops.bin()') typetest(y, [list, np.ndarray], 'y in ops.bin()') typetest(n, [int, float], 'n in ops.bin()') dimtest(x, [0], 'x in ops.bin()') dimtest(y, [len(x)], 'y in ops.bin()') minlength(x, 0, 'x in ops.bin()') minlength(x, 100, 'x in ops.bin()', warning_only=True) x_bin = [] y_bin = [] et = False if len(err) > 0: e_bin = [] et = True for i in range(0, int(len(x) - n - 1), n): x_bin.append(np.nanmean(x[i:i + n])) y_bin.append(np.nanmean(y[i:i + n])) if et: e_bin.append( np.sqrt(np.nansum(err[i:i + n]**2)) / len(err[i:i + n])) if et: return (np.array(x_bin), np.array(y_bin), np.array(e_bin)) else: return (np.array(x_bin), np.array(y_bin))
def v_orb(dp): """ This program calculates the orbital velocity in km/s for the planet in the data sequence provided in dp, the data-path. dp starts in the root folder, i.e. it starts with data/projectname/. This assumes a circular orbit. The output is a number in km/s. Parameters ---------- dp : str, path like The path to the dataset containing the config file. Returns ------- v_orb : float The planet's orbital velocity. list_of_sigmas_corrected : list List of 2D error matrices, telluric corrected. """ import numpy as np import pdb import astropy.units as u from tayph.vartests import typetest,postest dp=check_dp(dp)#Path object P=paramget('P',dp) r=paramget('a',dp) typetest(P,float,'P in sp.v_orb()') typetest(r,float,'r in sp.v_orb()') postest(P,'P in sp.v_orb()') postest(r,'r in sp.v_orb()') return (2.0*np.pi*r*u.AU/(P*u.d)).to('km/s').value
def read_shadow(dp, shadowname, rv, ccf): """ This reads shadow parameters from a pickle file generated by the fit_doppler_ model class below, and evaluates it on the user-supplied rv and ccf (the latter of which is just for shape-purposes and the mask defined during the creation of the shadow model. """ import pickle import os.path import sys from pathlib import Path import tayph.util as ut from tayph.vartests import typetest, dimtest import tayph.system_parameters as sp import numpy as np typetest(shadowname, str, 'shadowname in read_shadow().') typetest(rv, np.ndarray, 'rv in read_shadow()') typetest(ccf, np.ndarray, 'ccf in read_shadow()') dp = ut.check_path(dp, exists=True) inpath = dp / (shadowname + '.pkl') ut.check_path(inpath, exists=True) RVp = sp.RV(dp) pickle_in = open(inpath, "rb") d = pickle.load(pickle_in) params = d["fit_params"] T = d["Transit"] p = d["Phases"] aRstar = d["aRstar"] vsys = d["vsys"] i = d["inclination"] n_c = d["n_components"] maskHW = d["maskHW"] offset = d["offset"] #Offset of second component. S = d["S"] D = d["D"] RVp = sp.RV(dp) + vsys mask = mask_ccf_near_RV(rv, ccf, RVp, maskHW) return (evaluate_shadow(params, rv, ccf, T, p, aRstar, vsys, i, n_c, offset, S, D, leastsq=False), mask)
def transit(dp): """This code uses Ians astro python routines for the approximate Mandel & Agol transit lightcurve to produce the predicted transit lightcurve for the planet described by the configfile located at dp/config. This all assumes a circular orbit. =========== Derivation: =========== occultnonlin_small(z,p, cn) is the algorithm of the Mandel&Agol derivation. z = d/R_star, where d is the distance of the planet center to the LOS to the center of the star. sin(alpha) = d/a, with a the orbital distance (semi-major axis). so sin(alpha)*a/Rstar = d/a*a/Rstar = d/Rstar = z. a/Rstar happens to be a quantity that is well known from the transit light- curve. So z = sin(2pi phase)*a/Rstar. But this is in the limit of i = 90. From Cegla 2016 it follows that z = sqrt(xp^2 + yp^2). These are given as xp = a/Rstar sin(2pi phase) and yp = -a/Rstar * cos(2pi phase) * cos(i). The second quantity, p, is Rp/Rstar, also well known from the transit light- curve. cn is a four-element vector with the nonlinear limb darkening coefficients. If a shorter sequence is entered, the later values will be set to zero. By default I made it zero; i.e. the injected model does not take into account limb-darkening. """ from tayph.vartests import typetest import tayph.util as ut import tayph.iansastropy as iap import numpy as np import pdb dp=ut.check_path(dp) p=phase(dp) a_Rstar=paramget('aRstar',dp) Rp_Rstar=paramget('RpRstar',dp) i=paramget('inclination',dp) typetest(a_Rstar,float,'Rp_Rstar') typetest(a_Rstar,float,'a_Rstar') typetest(i,float,'i') xp=np.sin(p*2.0*np.pi)*a_Rstar yp=np.cos(p*2.0*np.pi)*np.cos(np.radians(i))*a_Rstar z=np.sqrt(xp**2.0 + yp**2.0) transit=iap.occultnonlin_small(z,Rp_Rstar,[0.0,0.0]) return transit
def write_mask_to_file(dp,maskname,list_of_masks_auto,list_of_masks_manual=[]): import sys from pathlib import Path import tayph.util as ut from tayph.vartests import typetest,dimtest import pdb ut.check_path(dp) typetest(maskname,str,'maskname in write_mask_to_file()') typetest(list_of_masks_auto,list,'list_of_masks_auto in write_mask_to_file()') typetest(list_of_masks_manual,list,'list_of_masks_manual in write_mask_to_file()') dimtest(list_of_masks_auto,[len(list_of_masks_manual),0,0],'list_of_masks_auto in write_mask_to_file()') outpath=Path(dp)/maskname if len(list_of_masks_auto) == 0 and len(list_of_masks_manual) == 0: print('RuntimeError in write_mask_to_file: Both lists of masks are emtpy!') sys.exit() print(f'---Saving lists of auto and manual masks to {str(outpath)}') if len(list_of_masks_auto) > 0: ut.save_stack(str(outpath)+'_auto.fits',list_of_masks_auto) if len(list_of_masks_manual) > 0: ut.save_stack(str(outpath)+'_manual.fits',list_of_masks_manual)