def run_pandexo_on_planet(Jmag,Teff,Rstar,Rp,dur,logg=4.5,FeH=0.0,JWST_mode='NIRSpec G395M',planetname=''): """This runs pandexo for a transit of a single planet in a given JWST mode. Input: Jmag, float: J-band magnitude (1.25 microns). Teff, float: Effective temperature of the host star in Kelvin. Rstar, float: Radius of the star in solar radii. Rp, float: Radius of the planet in Jupiter radii. dur, float: Transit duration in hours. logg, float: log(g) value of the star in cgs. FeH, float: Metallicity of the star [Fe/H]. JWST_mode, str: The mode of JWST to simulate. planetname, str: The name of the planet, just for printing to terminal.""" import warnings warnings.filterwarnings('ignore') import pandexo.engine.justdoit as jdi # THIS IS THE HOLY GRAIL OF PANDEXO import pandexo.engine.justplotit as jpi import pickle as pk import numpy as np import os import pdb import matplotlib.pyplot as plt #Check the input: NS_L = ['NIRSpec Prism'] NS_M = ['NIRSpec G140M','NIRSpec G235M','NIRSpec G395M'] NS_H = ['NIRSpec G140H','NIRSpec G235H','NIRSpec G395H'] NRSS = ['NIRISS SOSS'] MI = ['MIRI LRS'] NC = ['NIRCam F322W2', 'NIRCam F444W']#https://jwst-docs.stsci.edu/near-infrared-camera/nircam-observing-modes/nircam-time-series-observations/nircam-grism-time-series (very bright stars possible, ~5th magnitude) allowed_modes = NS_L+NS_M+NS_H+NRSS+MI+NC if JWST_mode not in allowed_modes: str = '' for m in allowed_modes: str+=m+',' raise ValueError("JWST_mode %s not in allowed modes %s"%(JWST_mode,str[0:-1])) #Available subarrays: #NIRSpec: sub1024a,sub1024b,sub2048,sub512 #NIRIS SOSS: substrip96,substrip256 #JDocs says for NIRSpec: #SUB1024A captures the shorter wavelength portion of the spectrum on the NRS1 detector, and the longer wavelength portion of the spectrum on NRS2. Alternatively, SUB1024B captures the longer wavelength portion on NRS1 and the shorter wavelength portion on NRS2. The medium resolution (M) grating and PRISM spectra for BOTS map entirely to NRS1. However, for the high resolution (H) gratings, the use of SUB1024B will enable capturing a (semi-)contiguous mid-wavelength portion of the spectrum across the detector gap. #SUB1024A should not be used with the PRISM since the spectra do not project to the region of the detector covered but this subarray. #And for SOSS: #SUBSTRIP96 for bright targets, samples only 1st order #SUBSTRIP256 SOSS mode default, samples orders 1–3 #For MIRI its only one option. if JWST_mode in NS_L+NS_M: subarray = 'sub1024b' if JWST_mode in NS_H: subarray = 'sub2048' if JWST_mode in NRSS: subarray = 'substrip96'#NIRISS SOSS for bright stars. if JWST_mode in MI: subarray = 'slitlessprism' if JWST_mode in NC: subarray = 'subgrism128' print('Running %s for %s and subarray %s.'%(planetname,JWST_mode,subarray)) #################### OBSERVATION INFORMATION exo_dict = jdi.load_exo_dict() exo_dict['observation']['sat_level'] = 80 exo_dict['observation']['sat_unit'] = '%' exo_dict['observation']['noccultations'] = 1 exo_dict['observation']['R'] = None exo_dict['observation']['baseline'] = 1.0 exo_dict['observation']['baseline_unit'] = 'frac' exo_dict['observation']['noise_floor'] = 0 #################### STAR INFORMATION exo_dict['star']['type'] = 'phoenix' exo_dict['star']['mag'] = Jmag exo_dict['star']['ref_wave'] = 1.25 #If ['mag'] in: J = 1.25, H = 1.6, K = 2.22 exo_dict['star']['temp'] = Teff exo_dict['star']['metal'] = FeH exo_dict['star']['logg'] = logg exo_dict['star']['radius'] = Rstar#In solar units. exo_dict['star']['r_unit'] = 'R_sun' #################### EXOPLANET INFORMATION exo_dict['planet']['type'] = 'constant' exo_dict['planet']['w_unit'] = 'um' exo_dict['planet']['radius'] = Rp exo_dict['planet']['r_unit'] = 'R_jup' exo_dict['planet']['transit_duration'] = dur exo_dict['planet']['td_unit'] = 'h' exo_dict['planet']['f_unit'] = 'rp^2/r*^2' #################### BEGIN RUN try: inst_dict = jdi.load_mode_dict(JWST_mode) inst_dict["configuration"]["detector"]["subarray"]=subarray except: print('Unknown error in selecting mode from instrument dictionary. Entering debug mode.') pdb.set_trace() try: jdi.run_pandexo(exo_dict,inst_dict, save_file=True, output_file='temp.p') except: print('Unknown error in running pandexo. Are the exoplanet system parameters physical? Entering debug mode.') pdb.set_trace() try: out = pk.load(open('temp.p','rb')) wl,spec,err= jpi.jwst_1d_spec(out,plot=False) except: print('Unknown error in collecting Pandexo output. Entering debug mode.') pdb.set_trace() return(np.nanmean(err[0]),out['timing']['Transit+Baseline, no overhead (hrs)'])
def main( planetdict, sat_level=80, sat_unit='%', noise_floor_ppm=20, \ useFirstOrbit=False, inst_modes=['WFC3 G141'], scan='Round Trip', \ subarray='GRISM512', nchan=14, norb='optimize', outdir='.' ): """ Routine called by the run_jwst.py script to run PandExo over specified modes for specified planet. """ t1 = time.time() # Prepare the output directory: cwd = os.getcwd() if outdir == '.': outdir = cwd planet_label = planetdict['name'] odirfull = os.path.join(outdir, planet_label) if os.path.isdir(odirfull) == False: os.makedirs(odirfull) # Identify the tepcat index: #ix = ( tepcat['names']==planet_label ) #if ix.sum()==0: # print( 'Could not match {0} to any TEPCat planets - skipping'.format( planet_label ) ) # return None # Prepare the PandExo inputs: z = jdi.load_exo_dict() z['observation']['sat_level'] = sat_level # default to 80% for basic run z['observation']['sat_unit'] = sat_unit z['observation']['noccultations'] = 1 # number of transits z['observation']['R'] = None # do not pre-bin the output spectra z['observation']['baseline'] = 1.0 # out-of-transit baseline quantity z['observation'][ 'baseline_unit'] = 'frac' # baseline quantity is fraction of time in transit versus out z['observation']['noise_floor'] = noise_floor_ppm # noise floor in p.p.m. z['star'] = planetdict['star'] z['planet'] = planetdict['planet'] z['planet']['type'] = 'constant' HST_ORB_PERIOD_DAYS = 96. / 60. / 24. tobs_days = 2 * z['planet']['transit_duration'] norb = int(np.ceil(tobs_days / HST_ORB_PERIOD_DAYS)) # Use a null spectrum for the planet: z['planet']['exopath'] = get_nullpath() if os.path.isfile(z['planet']['exopath']) == False: generate_nullspec() # Run PandExo over requested instrument modes: if inst_modes == 'all': inst_modes = ['WFC3 G141'] # G102 not implemented? nmodes = len(inst_modes) if nmodes == 1: modestr = '{0} instrument mode:\n'.format(nmodes) else: modestr = '{0} instrument modes:\n'.format(nmodes) for m in inst_modes: modestr += '{0}, '.format(m) print('\n{0}\nRunning PandExo for {1}\n{2}\n{0}\n'.format( 50 * '#', planet_label, modestr[:-2])) for k in range(nmodes): s1 = inst_modes[k].replace(' ', '-') if useFirstOrbit == True: s2 = 'keepFirstOrbit' else: s2 = 'dropFirstOrbit' if scan == 'Round Trip': s3 = 'RTscan' elif scan == 'Forward': s3 = 'Fscan' else: pdb.set_trace() oname = '{0}.{1}.{2}.{3}.txt'.format(s1, subarray, s2, s3) oname_obs = oname.replace('.txt', '.obspar.txt') opath = os.path.join(odirfull, oname) opath_obs = os.path.join(odirfull, oname_obs) wfc3 = jdi.load_mode_dict(inst_modes[k]) wfc3['configuration']['detector']['subarray'] = subarray wfc3['strategy']['calculateRamp'] = useFirstOrbit wfc3['strategy']['useFirstOrbit'] = useFirstOrbit if norb is not 'optimize': wfc3['strategy']['norbits'] = norb if useFirstOrbit == True: wfc3['strategy']['norbits'] = norb else: wfc3['strategy']['norbits'] = norb + 1 wfc3['strategy']['nchan'] = nchan wfc3['strategy']['schedulability'] = 100 wfc3['strategy']['scanDirection'] = scan y = jdi.run_pandexo(z, wfc3, save_file=False) #pdb.set_trace() #y = jdi.run_pandexo( z, [inst_modes[k]], save_file=False ) wav = y['planet_spec']['binwave'] err = (1e6) * y['planet_spec']['error'] * np.ones(nchan) outp = np.column_stack([wav, err]) np.savetxt(opath, outp) print('\nSaved noise: {0}'.format(opath, 50 * '#')) save_obspar(opath_obs, y) t2 = time.time() print('Total time taken = {0:.2f} minutes'.format((t2 - t1) / 60.)) return None
def run_pandexo(planetname='WASP-12 b', mode='transit', instrument='MIRI LRS', subarray=None, _modelspectrum=None, modelwave='um', ntransits=1, noise_floor=0., refband='k', plotres=30, mrmode='weiss2016', _outputpath='.', retpandexo=True, reddict=False): """Run a simulation of PandExo -- and avoid messing with scripts. INPUTS: planetname : str Name of a valid planet: 'WASP-12 b', 'HD 209458 b', 'GJ 9827 d', etc. Loaded from :func:`loadplanets` mode : str 'transit' or 'eclipse'. instrument : str Choose from: 'NIRCam F444W', 'NIRSpec Prism', 'NIRSpec G395M', 'NIRCam F322W2', 'NIRSpec G395H', 'NIRSpec G235H', 'NIRSpec G235M', 'NIRSpec G140M', 'NIRSpec G140H', 'MIRI LRS', 'NIRISS SOSS' (or 'WFC3 G141' for HST). subarray : str or None Detector subarray to use with your choice of instrument. Leave as None unless you know what you're doing. _modelspectrum : str or None Name of model spectrum to use. This should be a two-column, space-delimited ASCII file with wavelength in the first column and depth (transit or eclipse) in the second column. OR: If None, then assumes a simple, flat spectrum in transit (using size of planet) or eclipse (using equilibrium temperature of planet) _outputpath : str location where output pickle will be saved. modelwave : str Wavelength units (readable by AstroPy) for input _modelspectrum: "um","nm" ,"Angs", "secs" (for phase curves) ntransits : int Number of transits (or eclipses) to observe. noise_floor : scalar Assumed noise floor, as accepted for input by PandExo JWST simulator. This can be a fixed level or it can be a filepath refband : str 'j' or 'h' or 'k' for stellar flux. Magnitude is read from table of planet objects. plotres : int Final spectral resolution for returned simulated spectrum. mrmode : str Mass-Radius mode for estimating planet masses when they aren't known. See :func:`loadplanets` for more details. retpandexo : bool Whether to also return pandexo 'results' dictionary. retdict : bool Whether to also return the exo_dict and inst_dict inputs used to run PandExo (mainly for debugging/troubleshooting) :EXAMPLE: :: import jwst import pylab as py out = jwst.run_pandexo(planetname='WASP-12 b', mode='eclipse', instrument='NIRISS SOSS', retpandexo=True) py.figure() py.plot(out['result']['OriginalInput']['model_wave'], 1e6*out['result']['OriginalInput']['model_spec'], '-r') py.errorbar(out['wavelength'][0], 1e6*out['spectrum'][0], 1e6*out['uspectrum'][0], fmt='.k', elinewidth=2) py.ylabel('Depth [ppm]', fontsize=20) py.xlabel('Wavelength' , fontsize=20) py.minorticks_on() py.xlim(out['wavelength'][0].min()-0.1, out['wavelength'][0].max()+0.1) :TO-DO: Phase curve mode. User-specified planet parameters Modern error handling (instead of just crashing) """ # 2018-01-26 11:59 IJMC: Created knownmass = False planets = loadplanets(knownmass=knownmass, mrmode=mrmode) planetindex = planets.pl_name == planetname if not planetindex.any(): print "Planet %s not found -- bombing out" % planetname stopp elif planetindex.sum() > 1: print "More than one copy of planet %s found in table -- check your file. Bombing out." % planetname stopp else: planet = planets[planetindex] # Create filename: iter = 0 outputfilename = ( 'PE_%s_%s_%s_%s_n%i_%04i.pickle' % (planetname, mode, instrument, subarray, ntransits, iter)).replace( ' ', '_') if _modelspectrum is None or _modelspectrum == 'constant': outputfilename = outputfilename.replace(mode, mode + '-constant') while os.path.isfile(outputfilename): iter += 1 outputfilename = outputfilename.replace('%04i.pickle' % (iter - 1), '%04i.pickle' % iter) # Set other options (under the hood): if refband.lower() == 'k': ref_wave = 2.2 elif refband.lower() == 'h': ref_wave = 1.6 elif refband.lower() == 'j': ref_wave = 1.25 mag = float(getattr(planet, 'st_' + refband)) if hasattr(planet, 'st_metallicity'): # what is it really called? metal = float(planet.st_metallicity) else: metal = 0. if mode == 'transit': f_unit = 'rp^2/r*^2' elif mode == 'eclipse': f_unit = 'fp/f*' else: print "I don't know mode '%s' -- bombing out" % mode stopp # Pandexo: Begin! exo_dict = jdi.load_exo_dict() exo_dict['observation'][ 'sat_level'] = 80 #saturation level in percent of full well exo_dict['observation'][ 'sat_unit'] = '%' # other option = 'e' for electrons exo_dict['observation']['noccultations'] = ntransits exo_dict['observation'][ 'baseline'] = 4.0 * 60.0 * 60.0 #time spent observing out of transit, make sure to speciy units exo_dict['observation'][ 'baseline_unit'] = 'total' #total obersving time, other option 'frac' = in/out exo_dict['observation']['noise_floor'] = noise_floor exo_dict['star']['type'] = 'phoenix' exo_dict['star']['mag'] = mag exo_dict['star']['ref_wave'] = ref_wave exo_dict['star']['temp'] = int(planet.Teff) exo_dict['star']['metal'] = float(metal) exo_dict['star']['logg'] = 2+np.log10(float(an.G*an.msun*planet.st_mass / \ (an.rsun*planet.st_rad)**2)) exo_dict['star']['radius'] = float(planet.st_rad) exo_dict['star']['r_unit'] = 'R_sun' if _modelspectrum is None or _modelspectrum == 'constant': exo_dict['planet']['type'] = 'constant' if mode == 'transit': exo_dict['planet']['f_unit'] = 'rp^2/r*^2' elif mode == 'eclipse': exo_dict['planet']['f_unit'] = 'fp/f*' else: exo_dict['planet']['type'] = 'user' exo_dict['planet']['exopath'] = _modelspectrum exo_dict['planet']['w_unit'] = modelwave exo_dict['planet']['f_unit'] = f_unit exo_dict['planet']['transit_duration'] = float(planet.pl_trandur * 24 * 3600.) exo_dict['planet']['td_unit'] = 's' exo_dict['planet']['temp'] = float(planet.Teq) exo_dict['planet']['radius'] = float(planet.pl_radj) exo_dict['planet']['r_unit'] = 'R_jup' exo_dict['planet']['i'] = float(planet.pl_orbincl) exo_dict['planet']['ars'] = float( (planet.pl_orbsmax * an.AU) / (planet.st_rad * an.rsun)) exo_dict['planet']['period'] = float(planet.pl_orbper) # Now load in the instrument: inst_dict = jdi.load_mode_dict(instrument) if subarray is not None: inst_dict['configuration']['detector']['subarray'] = subarray inst_dict['configuration']['detector']['nsamp'] = None inst_dict['configuration']['detector']['samp_seq'] = None result = jdi.run_pandexo(exo_dict, inst_dict, output_file=_outputpath + outputfilename) if not np.isfinite( [result['timing'][key] for key in result['timing'].keys()]).all(): print "Something went wrong with simulation. Maybe star is too bright? Bombing out." stopp x, y, e = jpi.jwst_1d_spec(result, R=plotres, model=True, plot=False) #, x_range=[.8,1.28]) # num_tran=10 # Prepare dictionary for returning to user: ret = dict(wavelength=x, spectrum=y, uspectrum=e) if retpandexo: ret['result'] = result ret['outputfile'] = _outputpath + outputfilename if reddict: ret['exo_dict'] = exo_dict ret['inst_dict'] = inst_dict return ret
def main( planet_label, tepcat, sat_level=80, sat_unit='%', noise_floor_ppm=20, \ inst_modes='all', outdir='.' ): """ Routine called by the run_jwst.py script to run PandExo over specified modes for specified planet. """ t1 = time.time() # Prepare the output directory: cwd = os.getcwd() if outdir == '.': outdir = cwd odirfull = os.path.join(outdir, planet_label) if os.path.isdir(odirfull) == False: os.makedirs(odirfull) # Identify the tepcat index: ix = (tepcat['names'] == planet_label) if ix.sum() == 0: print('Could not match {0} to any TEPCat planets - skipping'.format( planet_label)) return None # Prepare the PandExo inputs: z = jdi.load_exo_dict() z['observation']['sat_level'] = sat_level # default to 80% for basic run z['observation']['sat_unit'] = sat_unit z['observation']['noccultations'] = 1 # number of transits z['observation']['R'] = None # do not pre-bin the output spectra z['observation']['baseline'] = 1.0 # out-of-transit baseline quantity z['observation'][ 'baseline_unit'] = 'frac' # baseline quantity is fraction of time in transit versus out z['observation']['noise_floor'] = noise_floor_ppm # noise floor in p.p.m. z['star']['type'] = 'phoenix' # use the provided Phoenix spectra z['star']['mag'] = float(tepcat['kmags'][ix]) # stellar magnitude z['star']['ref_wave'] = 2.22 # K band central wavelength in micron z['star']['temp'] = float( tepcat['tstar'][ix]) # stellar effective temperature in Kelvin z['star']['metal'] = float( tepcat['metalstar'][ix]) # stellar metallicity as log10( [Fe/H] ) z['star']['logg'] = float( tepcat['loggstar'] [ix]) # stellar surface gravity as log10( g (c.g.s.) ) z['star']['r_unit'] = 'R_sun' # stellar radius unit z['planet']['r_unit'] = 'R_jup' # planet radius unit z['planet'][ 'w_unit'] = 'um' # wavelength unit is micron; other options include 'Angs', secs" (for phase curves) z['planet']['f_unit'] = 'fp/f*' # options are 'rp^2/r*^2' or 'fp/f*' #z['planet']['transit_duration'] = float( tepcat['tdurs'][ix] )*24.*60.*60. # transit duration in seconds #z['planet']['td_unit'] = 's' # transit duration unit z['planet']['transit_duration'] = float( tepcat['tdurs'][ix]) # transit duration in days z['planet']['td_unit'] = 'd' # transit duration unit # Use a null spectrum for the planet: z['planet']['exopath'] = get_nullpath() if os.path.isfile(z['planet']['exopath']) == False: generate_nullspec() # Run PandExo over requested instrument modes: if inst_modes == 'all': inst_modes = list(jdi.ALL.keys()) inst_modes.remove('WFC3 G141') # remove HST modes nmodes = len(inst_modes) if nmodes == 1: modestr = '{0} instrument mode:\n'.format(nmodes) else: modestr = '{0} instrument modes:\n'.format(nmodes) for m in inst_modes: modestr += '{0}, '.format(m) print('\n{0}\nRunning PandExo for {1}\n{2}\n{0}\n'.format( 50 * '#', planet_label, modestr[:-2])) for k in range(nmodes): # G140 has two filters, need to run the second (non-default) manually; the first # is run by default when inst_modes[k] string passed to run_pandexo() below: if 'G140' in inst_modes[k]: # G140 non-default: g140 = jdi.load_mode_dict(inst_modes[k]) g140['configuration']['instrument']['filter'] = 'f100lp' oname = '{0}-F100LP.txt'.format(inst_modes[k].replace(' ', '-')) oname_obs = '{0}-F100LP.obspar.txt'.format(inst_modes[k].replace( ' ', '-')) opath = os.path.join(odirfull, oname) opath_obs = os.path.join(odirfull, oname_obs) y = jdi.run_pandexo(z, g140, save_file=False) wav = y['FinalSpectrum']['wave'] err = y['FinalSpectrum']['error_w_floor'] * (1e6) outp = np.column_stack([wav, err]) np.savetxt(opath, outp) print('\nSaved noise: {0}'.format(opath, 50 * '#')) save_obspar(opath_obs, y) # G140 prepare default: oname = '{0}-F070LP.txt'.format(inst_modes[k].replace(' ', '-')) oname_obs = '{0}-F070LP.obspar.txt'.format(inst_modes[k].replace( ' ', '-')) else: oname = '{0}.txt'.format(inst_modes[k].replace(' ', '-')) oname_obs = '{0}.obspar.txt'.format(inst_modes[k].replace( ' ', '-')) opath = os.path.join(odirfull, oname) opath_obs = os.path.join(odirfull, oname_obs) y = jdi.run_pandexo(z, [inst_modes[k]], save_file=False) wav = y['FinalSpectrum']['wave'] err = y['FinalSpectrum']['error_w_floor'] * (1e6) outp = np.column_stack([wav, err]) np.savetxt(opath, outp) print('\nSaved noise: {0}'.format(opath, 50 * '#')) save_obspar(opath_obs, y) t2 = time.time() print('Total time taken = {0:.2f} minutes'.format((t2 - t1) / 60.)) return None