예제 #1
0
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)'])
예제 #2
0
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
예제 #3
0
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
예제 #4
0
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