def get_coord(targ_file, radec=None): ''' radec: int (None) None: Read from ASCII file 1: List of [Name, RA, DEC] with RA/DEC as : separated strings 2: List of [Name, RA, DEC] with RA/DEC as decimal degrees ''' if not isinstance(targ_file,basestring): raise IOError('Bad input to finder.get_coord!') from astropy.io import ascii # Import Tables if radec == None: # Read ra_tab = ascii.read(targ_file) #, names=('Name','RA','DEC','Epoch')) # Rename the columns ra_tab.rename_column('col1','Name') if isinstance(ra_tab['col2'][0],basestring): ra_tab.rename_column('col2','RAS') ra_tab.rename_column('col3','DECS') else: ra_tab.rename_column('col2','RA') ra_tab.rename_column('col3','DEC') elif radec == 1: # Error check if len(targ_file) != 3: return -1 # Manipulate arr = np.array(targ_file).reshape(1,3) # Generate the Table ra_tab = QTable( arr, names=('Name','RAS','DECS') ) elif radec == 2: # Error check if len(targ_file) != 3: return -1 # Manipulate ras, decs = x_radec.dtos1((targ_file[1], targ_file[2])) # Generate the Table ra_tab = QTable( [ [targ_file[0]], [ras], [decs] ], names=('Name','RA','DEC') ) else: raise ValueError('get_coord: Bad flag') # Add dummy columns for decimal degrees and EPOCH nrow = len(ra_tab) col_RAD = Column(name='RAD', data=np.zeros(nrow), unit=u.degree) col_DECD = Column(name='DECD', data=np.zeros(nrow), unit=u.degree) col_EPOCH = Column(name='EPOCH', data=np.zeros(nrow)) ra_tab.add_columns( [col_RAD, col_DECD, col_EPOCH] ) # Assume 2000 for now ra_tab['EPOCH'] = 2000. return ra_tab
def test_add_column(mixin_cols): """ Test that adding a column preserves values and attributes """ attrs = ('name', 'unit', 'dtype', 'format', 'description', 'meta') m = mixin_cols['m'] assert m.info.name is None # Make sure adding column in various ways doesn't touch t = QTable([m], names=['a']) assert m.info.name is None t['new'] = m assert m.info.name is None m.info.name = 'm' m.info.format = '{0}' m.info.description = 'd' m.info.meta = {'a': 1} t = QTable([m]) # Add columns m2, m3, m4 by two different methods and test expected equality t['m2'] = m m.info.name = 'm3' t.add_columns([m], copy=True) m.info.name = 'm4' t.add_columns([m], copy=False) for name in ('m2', 'm3', 'm4'): assert_table_name_col_equal(t, name, m) for attr in attrs: if attr != 'name': assert getattr(t['m'].info, attr) == getattr(t[name].info, attr) # Also check that one can set using a scalar. s = m[0] if type(s) is type(m) and 'info' in s.__dict__: # We're not going to worry about testing classes for which scalars # are a different class than the real array, or where info is not copied. t['s'] = m[0] assert_table_name_col_equal(t, 's', m[0]) for attr in attrs: if attr != 'name': assert getattr(t['m'].info, attr) == getattr(t['s'].info, attr) # While we're add it, also check a length-1 table. t = QTable([m[1:2]], names=['m']) if type(s) is type(m) and 'info' in s.__dict__: t['s'] = m[0] assert_table_name_col_equal(t, 's', m[0]) for attr in attrs: if attr != 'name': assert getattr(t['m'].info, attr) == getattr(t['s'].info, attr)
def test_add_column(mixin_cols): """ Test that adding a column preserves values and attributes """ attrs = ('name', 'unit', 'dtype', 'format', 'description', 'meta') m = mixin_cols['m'] assert m.info.name is None # Make sure adding column in various ways doesn't touch t = QTable([m], names=['a']) assert m.info.name is None t['new'] = m assert m.info.name is None m.info.name = 'm' m.info.format = '{0}' m.info.description = 'd' m.info.meta = {'a': 1} t = QTable([m]) # Add columns m2, m3, m4 by two different methods and test expected equality t['m2'] = m m.info.name = 'm3' t.add_columns([m], copy=True) m.info.name = 'm4' t.add_columns([m], copy=False) for name in ('m2', 'm3', 'm4'): assert_table_name_col_equal(t, name, m) for attr in attrs: if attr != 'name': assert getattr(t['m'].info, attr) == getattr(t[name].info, attr) # Also check that one can set using a scalar. s = m[0] if type(s) is type(m): # We're not going to worry about testing classes for which scalars # are a different class than the real array (and thus loose info, etc.) t['s'] = m[0] assert_table_name_col_equal(t, 's', m[0]) for attr in attrs: if attr != 'name': assert getattr(t['m'].info, attr) == getattr(t['s'].info, attr) # While we're add it, also check a length-1 table. t = QTable([m[1:2]], names=['m']) if type(s) is type(m): t['s'] = m[0] assert_table_name_col_equal(t, 's', m[0]) for attr in attrs: if attr != 'name': assert getattr(t['m'].info, attr) == getattr(t['s'].info, attr)
def get_coord(targ_file, radec=None): ''' radec: int (None) None: Read from ASCII file 1: List of [Name, RA, DEC] with RA/DEC as : separated strings 2: List of [Name, RA, DEC] with RA/DEC as decimal degrees ''' from astropy.io import ascii # Import Tables if radec == None: # Read ra_tab = ascii.read(targ_file) #, names=('Name','RA','DEC','Epoch')) # Rename the columns ra_tab.rename_column('col1', 'Name') ra_tab.rename_column('col2', 'RA') ra_tab.rename_column('col3', 'DEC') elif radec == 1: # Error check if len(targ_file) != 3: return -1 # Manipulate arr = np.array(targ_file).reshape(1, 3) # Generate the Table ra_tab = QTable(arr, names=('Name', 'RAS', 'DECS')) elif radec == 2: # Error check if len(targ_file) != 3: return -1 # Manipulate ras, decs = x_radec.dtos1((targ_file[1], targ_file[2])) # Generate the Table ra_tab = QTable([[targ_file[0]], [ras], [decs]], names=('Name', 'RA', 'DEC')) else: raise ValueError('get_coord: Bad flag') # Add dummy columns for decimal degrees and EPOCH nrow = len(ra_tab) col_RAD = Column(name='RAD', data=np.zeros(nrow), unit=u.degree) col_DECD = Column(name='DECD', data=np.zeros(nrow), unit=u.degree) col_EPOCH = Column(name='EPOCH', data=np.zeros(nrow)) ra_tab.add_columns([col_RAD, col_DECD, col_EPOCH]) # Assume 2000 for now ra_tab['EPOCH'] = 2000. return ra_tab
def mk_summary(dlas, prefix, outfil, specpath=None, htmlfil=None): """ Loops through the DLA list and generates a Table Also pushes the 1D spectra into the folder Parameters ---------- dlas : DLASurvey prefix : str outfil : str Name of the output FITS summary file htmlfil : str, optional Returns ------- """ # if htmlfil is None: htmlfil = 'tmp.html' # # Constructing # QSO, RA/DEC cqso = Column(dlas.qso, name='QSO') ra = dlas.coord.ra.degree dec = dlas.coord.dec.degree jname = [] for abs_sys in dlas._abs_sys: jname.append(survey_name(prefix, abs_sys)) cjname = Column(jname, name='Name') cra = Column(ra, name='RA', unit=u.degree) cdec = Column(dec, name='DEC', unit=u.degree) czem = Column(dlas.zem, name='Z_QSO') # Begin the Table dla_table = QTable([cjname, cqso, cra, cdec, czem]) # LLS properties czabs = Column(dlas.zabs, name='ZABS') cNHI = Column(dlas.NHI, name='logNHI') csigNHI = Column(dlas.sig_NHI, name='sig(logNHI)') # Add to Table dla_table.add_columns([czabs, cNHI, csigNHI]) # Spectra files all_sfiles = [] for jj, ills in enumerate(dlas._abs_sys): sub_spec = mk_1dspec(ills, name=cjname[jj], outpath=specpath) # Pad while len(sub_spec) < 5: sub_spec.append(str('NULL')) # Append all_sfiles.append(sub_spec) cspec = Column(np.array(all_sfiles), name='SPEC_FILES') dla_table.add_column(cspec) # Sort dla_table.sort('RA') # Write print('Writing {:s}'.format(outfil)) xxf.table_to_fits(dla_table, outfil) print('Writing {:s}'.format(htmlfil)) Table(dla_table).write(htmlfil) return dla_table
def mk_summary(dlas, prefix, outfil, specpath=None, htmlfil=None): """ Loops through the DLA list and generates a Table Also pushes the 1D spectra into the folder Parameters ---------- dlas : DLASurvey prefix : str outfil : str Name of the output FITS summary file htmlfil : str, optional Returns ------- """ # if htmlfil is None: htmlfil = 'tmp.html' # # Constructing # QSO, RA/DEC cqso = Column(dlas.qso, name='QSO') ra = dlas.coord.ra.degree[0] dec = dlas.coord.dec.degree[0] jname = [] for abs_sys in dlas._abs_sys: jname.append(survey_name(prefix, abs_sys)) cjname = Column(jname, name='Name') cra = Column(ra, name='RA', unit=u.degree) cdec = Column(dec, name='DEC', unit=u.degree) czem = Column(dlas.zem, name='Z_QSO') # Begin the Table dla_table = QTable( [cjname, cqso, cra, cdec, czem] ) # LLS properties czabs = Column(dlas.zabs, name='ZABS') cNHI = Column(dlas.NHI, name='logNHI') csigNHI = Column(dlas.sig_NHI, name='sig(logNHI)') # Add to Table dla_table.add_columns([czabs, cNHI, csigNHI]) # Spectra files all_sfiles = [] for jj,ills in enumerate(dlas._abs_sys): sub_spec = mk_1dspec(ills, name=cjname[jj], outpath=specpath) # Pad while len(sub_spec) < 5: sub_spec.append(str('NULL')) # Append all_sfiles.append(sub_spec) cspec = Column(np.array(all_sfiles), name='SPEC_FILES') dla_table.add_column( cspec ) # Sort dla_table.sort('RA') # Write print('Writing {:s}'.format(outfil)) xxf.table_to_fits(dla_table,outfil) print('Writing {:s}'.format(htmlfil)) Table(dla_table).write(htmlfil) return dla_table
def Load_ExoArchive_Universe(self, composite_table=True, force_new_pull=False, fill_empties=True): ''' A function that reads the Exoplanet Archive data to populate the planet table Unless force_new_pull=True: If the filename provided in constructor is new, new data is pulled from the archive If the filename already exists, we try to load that file as an astroquery QTable Kwargs: composite_table - Bool. True [default]: pull "Planetary Systems Composite Parameters Table". False: pull simple "Planetary Systems" Table NOTE: see Archive website for difference between these tables force_new_pull - Bool. False [default]: loads table from filename if filename file exists. True: pull new archive data and overwrite filename fill_empties - Bool. True [default]: approximate empty table values using other values present in data. Ex: radius, mass, logg, angsep, etc. NOTE: When composite_table=True we do not approximate the planet radius or mass; we keep the archive-computed approx. Approximation methods: - AngSep - theta[mas] = SMA[au]/distance[pc] * 1e3 - logg - logg [log(cgs)] = log10(G*mass/radius**2) - StarLum - absVmag = Vmag - 5*log10(distance[pc]/10) starlum[L/Lsun] = 10**-(absVmag-4.83)/2.5 - StarRad - rad[Rsun] = (5800/Teff[K])**2 *sqrt(starlum) - PlanetRad - ** when composite_table=True, keep archive-computed approx Based on Thorngren 2019 and Chen&Kipping 2016 - PlanetMass - ^^ Inverse of PlanetRad *** Note: the resulting planet table will have nan's where data is missing/unknown. Ex. if a planet lacks a radius val, the 'PlanetRadius' for will be np.nan ''' #-- Define columns to read. NOTE: add columns here if needed. # col2pull entries should be matched with colNewNames entries col2pull = "pl_name,hostname,pl_orbsmax,pl_orbeccen,pl_orbincl,pl_bmasse,pl_rade," + \ "pl_eqt,ra,dec,sy_dist,st_spectype,st_mass,st_teff," + \ "st_rad,st_logg,st_lum,st_age,st_vsin,st_radv," + \ "st_met,sy_plx,sy_bmag,sy_vmag,sy_rmag,sy_icmag," + \ "sy_jmag,sy_hmag,sy_kmag,discoverymethod" colNewNames = ["PlanetName","StarName","SMA","Ecc","Inc","PlanetMass","PlanetRadius", "PlanetTeq","RA","Dec","Distance","StarSpT","StarMass","StarTeff", "StarRad","StarLogg","StarLum","StarAge","StarVsini","StarRadialVelocity", "StarZ","StarParallax","StarBMag","StarVmag","StarRmag","StarImag", "StarJmag","StarHmag","StarKmag","DiscoveryMethod"] #-- Load/Pull data depending on provided filename import os if os.path.isfile(self.filename) and not force_new_pull: # Existing filename was provided so let's try use that print("%s already exists:\n we'll attempt to read this file as an astropy QTable"%self.filename) NArx_table = QTable.read(self.filename, format='ascii.ecsv') # Check that the provided table file matches the requested table type if NArx_table.meta['isPSCOMPPARS'] != composite_table: err0 = '%s contained the wrong table-type:'%self.filename err1 = 'pscomppars' if composite_table else 'ps' err2 = 'pscomppars' if NArx_table.meta['isPSCOMPPARS'] else 'ps' err3 = " Expected '{}' table but found '{}' table.".format(err1,err2) err4 = ' Consider setting force_new_pull=True.' raise ValueError(err0+err3+err4) else: # New filename was provided or a new pull was explicitly requested. Pull new data if not force_new_pull: print("%s does not exist:\n we'll pull new data from the archive and save it to this filename"%self.filename) else: print("%s may or may not exist:\n force_new_pull=True so we'll pull new data regardless and overwrite as needed"%self.filename) # Import pyVO package used to query the Exoplanet Archive import pyvo as vo # Create a "service" which can be used to access the archive TAP server NArx_service = vo.dal.TAPService("https://exoplanetarchive.ipac.caltech.edu/TAP") # Create a "query" string formatted per the TAP specifications # 'select': specify which columns to pull # 'from': specify which table to pull # 'where': (optional) specify parameters to be met when choosing what to pull # Add where flag for ps to only pull the best row for each planet tab2pull = "pscomppars" if composite_table else "ps where default_flag=1" query = "select "+col2pull+" from "+tab2pull # Pull the data and convert to astropy masked QTable NArx_res = NArx_service.search(query) NArx_table = QTable(NArx_res.to_table()) # Add a flag to the table metadata to denote what kind of table it was # This'll prevent trying to read the table as the wrong type later NArx_table.meta['isPSCOMPPARS'] = composite_table # Save raw table for future use NArx_table.write(self.filename,format='ascii.ecsv',overwrite=force_new_pull) # Read table back in to ensure that formatting from a fresh pull matches # the formatting from an old pull (as done when filename exists) NArx_table = QTable.read(self.filename, format='ascii.ecsv') #-- Rename columns to psisim-expected names NArx_table.rename_columns(col2pull.split(','),colNewNames) #-- Change fill value from default 1e20 to np.nan for col in NArx_table.colnames: if isinstance(NArx_table[col],MaskedColumn) and isinstance(NArx_table[col].fill_value,(int,float)): # Only change numeric fill values to nan NArx_table[col].fill_value = np.nan #-- Add new columns for values not easily available or computable from table # TODO: for now, these are masked but we should find a good way to populate them NArx_table.add_columns([MaskedColumn(length=len(NArx_table),mask=True,fill_value=np.nan)]*3, names=['Flux Ratio','ProjAU','Phase']) if fill_empties: #-- Compute missing planet columns # Compute missing masses and radii using mass-radius relations if not composite_table: # NOTE: composite table already has radius-mass approximation so we'll # only repeat them if we don't pull that table # Convert masked columns to ndarrays with 0's instead of mask # as needed by the approximate_... functions masses = np.array(NArx_table['PlanetMass'].filled(fill_value=0.0)) radii = np.array(NArx_table['PlanetRadius'].filled(fill_value=0.0)) eqtemps = np.array(NArx_table['PlanetTeq'].filled(fill_value=0.0)) # Perform approximations radii = self.approximate_radii(masses,radii,eqtemps) masses = self.approximate_masses(masses,radii,eqtemps) # Create masks for non-zero values (0's are values where data was missing) rad_mask = (radii != 0.) mss_mask = (masses != 0.) # Create mask to only missing values in NArx_table with valid values rad_mask = NArx_table['PlanetRadius'].mask & rad_mask mss_mask = NArx_table['PlanetMass'].mask & mss_mask # Place results back in the table NArx_table['PlanetRadius'][rad_mask] = radii[rad_mask] NArx_table['PlanetMass'][mss_mask] = masses[mss_mask] # Angular separation NArx_table['AngSep'] = NArx_table['SMA']/NArx_table['Distance'] * 1e3 # Planet logg grav = constants.G * (NArx_table['PlanetMass'].filled()*u.earthMass) / (NArx_table['PlanetRadius'].filled()*u.earthRad)**2 NArx_table['PlanetLogg'] = np.ma.log10(MaskedColumn(np.ma.masked_invalid(grav.cgs.value),fill_value=np.nan)) # logg cgs #-- Guess star luminosity, radius, and gravity for missing (masked) values only # The guesses will be questionably reliabile # Star Luminosity host_MVs = NArx_table['StarVmag'] - 5*np.ma.log10(NArx_table['Distance']/10) # absolute v mag host_lum = -(host_MVs-4.83)/2.5 #log10(L/Lsun) NArx_table['StarLum'][NArx_table['StarLum'].mask] = host_lum[NArx_table['StarLum'].mask] # Star radius host_rad = (5800/NArx_table['StarTeff'])**2 *np.ma.sqrt(10**NArx_table['StarLum']) # Rsun NArx_table['StarRad'][NArx_table['StarRad'].mask] = host_rad[NArx_table['StarRad'].mask] # Star logg host_grav = constants.G * (NArx_table['StarMass'].filled()*u.solMass) / (NArx_table['StarRad'].filled()*u.solRad)**2 host_logg = np.ma.log10(np.ma.masked_invalid(host_grav.cgs.value)) # logg cgs NArx_table['StarLogg'][NArx_table['StarLogg'].mask] = host_logg[NArx_table['StarLogg'].mask] else: # Create fully masked columns for AngSep and PlanetLogg NArx_table.add_columns([MaskedColumn(length=len(NArx_table),mask=True,fill_value=np.nan)]*2, names=['AngSep','PlanetLogg']) #-- Deal with units (conversions and Quantity multiplications) # Set host luminosity to L/Lsun from log10(L/Lsun) NArx_table['StarLum'] = 10**NArx_table['StarLum'] # L/Lsun # Make sure all number fill_values are np.nan after the column manipulations for col in NArx_table.colnames: if isinstance(NArx_table[col],MaskedColumn) and isinstance(NArx_table[col].fill_value,(int,float)): # Only change numeric fill values to nan NArx_table[col].fill_value = np.nan # Fill in masked values NArx_table = NArx_table.filled() # Apply units NArx_table['SMA'] *= u.AU NArx_table['Inc'] *= u.deg NArx_table['PlanetMass'] *= u.earthMass NArx_table['PlanetRadius'] *= u.earthRad NArx_table['PlanetTeq'] *= u.K NArx_table['RA'] *= u.deg NArx_table['Dec'] *= u.deg NArx_table['Distance'] *= u.pc NArx_table['StarMass'] *= u.solMass NArx_table['StarTeff'] *= u.K NArx_table['StarRad'] *= u.solRad NArx_table['StarLogg'] *= u.dex(u.cm/(u.s**2)) NArx_table['StarLum'] *= u.solLum NArx_table['StarAge'] *= u.Gyr NArx_table['StarVsini'] *= u.km/u.s NArx_table['StarRadialVelocity'] *= u.km/u.s #NArx_table['StarZ'] *= u.dex NArx_table['StarParallax'] *= u.mas NArx_table['ProjAU'] *= u.AU NArx_table['Phase'] *= u.rad NArx_table['AngSep'] *= u.mas NArx_table['PlanetLogg'] *= u.dex(u.cm/(u.s**2)) self.planets = NArx_table