Beispiel #1
0
def perform_out(pandeia_input, pandexo_input,timing, both_spec):
    """Runs pandeia for the out of transit data
    
    Parameters
    ----------
    pandeia_input : dict 
        pandeia specific input info 
    pandexo_input : dict 
        exoplanet specific observation info 
    timing : dict 
        timing dictionary from **compute_timing** 
    both_spec : dict 
        dictionary transit spectra computed from **createInput.bothTrans** 

    Returns
    -------
    dict 
        pandeia output dictionary for out of transit data 
    """
    #pandeia inputs, simulate one integration at a time 
    pandeia_input['configuration']['detector']['ngroup'] = timing['APT: Num Groups per Integration']
    pandeia_input['configuration']['detector']['nint'] = timing['Num Integrations Out of Transit']
    pandeia_input['configuration']['detector']['nexp'] = 1 

    report_out = perform_calculation(pandeia_input, dict_report=False)
    
    return report_out
Beispiel #2
0
def perform_out(pandeia_input, pandexo_input,timing, both_spec):
    """Runs pandeia for the out of transit data
    
    Parameters
    ----------
    pandeia_input : dict 
        pandeia specific input info 
    pandexo_input : dict 
        exoplanet specific observation info 
    timing : dict 
        timing dictionary from **compute_timing** 
    both_spec : dict 
        dictionary transit spectra computed from **createInput.bothTrans** 

    Returns
    -------
    dict 
        pandeia output dictionary for out of transit data 
    """
    #pandeia inputs, simulate one integration at a time 
    pandeia_input['configuration']['detector']['ngroup'] = timing['APT: Num Groups per Integration']
    pandeia_input['configuration']['detector']['nint'] = timing['Num Integrations Out of Transit']
    pandeia_input['configuration']['detector']['nexp'] = 1 

    report_out = perform_calculation(pandeia_input, dict_report=False)
    
    return report_out
Beispiel #3
0
def perform_in(pandeia_input, pandexo_input,timing, both_spec, out, calculation): 
    """Computes in transit data 
    
    Runs Pandeia for the in transit data or computes the in transit simulation 
    from the out of transit pandeia run 
    
    Parameters
    ----------
    pandeia_input : dict 
        pandeia specific input info 
    pandexo_input : dict 
        exoplanet specific observation info 
    timing : dict 
        timing dictionary from **compute_timing** 
    both_spec : dict 
        dictionary transit spectra computed from **createInput.bothTrans** 
    out : dict 
        out of transit dictionary from **perform_in**
    calculation : str
        key which speficies the kind of noise calcualtion 
        (2d extract, slope method, fml, phase_spec). 
        Recommended for transit transmisstion spectra = fml

    Returns
    -------
    dict
        pandeia output dictionary 
    """
    
    #function to run pandeia for in transit
    if calculation == 'phase_spec':
        #return the phase curve since it's all we need 
        report_in = {'time': both_spec['time'],'planet_phase': both_spec['planet_phase']}
    elif calculation == 'fml':
        #for FML method, we only use the flux rate calculated in pandeia so 
        #can compute in transit flux rate without running pandeia a third time     
        report_in = deepcopy(out)
        
        transit_depth = np.interp(report_in['1d']['extracted_flux'][0],
                                    both_spec['wave'], both_spec['frac'])
        report_in['1d']['extracted_flux'][1] = report_in['1d']['extracted_flux'][1]*transit_depth
    else: 
        #only run pandeia a third time if doing slope method and need accurate run for the 
        #nint and timing
        pandeia_input['configuration']['detector']['ngroup'] = timing['APT: Num Groups per Integration']
        pandeia_input['configuration']['detector']['nint'] = timing['Num Integrations In Transit']
        pandeia_input['configuration']['detector']['nexp'] = 1
  
        in_transit_spec = np.array([both_spec['wave'], both_spec['flux_in_trans']])
    
        pandeia_input['scene'][0]['spectrum']['sed']['spectrum'] = in_transit_spec

        report_in = perform_calculation(pandeia_input, dict_report=True)
        instrument = pandeia_input['configuration']['instrument']['instrument']
        #remove QY effects 
        report_in = remove_QY(report_in, instrument)
        report_in.pop('3d')
    
    return report_in
Beispiel #4
0
def perform_in(pandeia_input, pandexo_input,timing, both_spec, out, calculation): 
    """Computes in transit data 
    
    Runs Pandeia for the in transit data or computes the in transit simulation 
    from the out of transit pandeia run 
    
    Parameters
    ----------
    pandeia_input : dict 
        pandeia specific input info 
    pandexo_input : dict 
        exoplanet specific observation info 
    timing : dict 
        timing dictionary from **compute_timing** 
    both_spec : dict 
        dictionary transit spectra computed from **createInput.bothTrans** 
    out : dict 
        out of transit dictionary from **perform_in**
    calculation : str
        key which speficies the kind of noise calcualtion 
        (2d extract, slope method, fml, phase_spec). 
        Recommended for transit transmisstion spectra = fml

    Returns
    -------
    dict
        pandeia output dictionary 
    """
    
    #function to run pandeia for in transit
    if calculation == 'phase_spec':
        #return the phase curve since it's all we need 
        report_in = {'time': both_spec['time'],'planet_phase': both_spec['planet_phase']}
    elif calculation == 'fml':
        #for FML method, we only use the flux rate calculated in pandeia so 
        #can compute in transit flux rate without running pandeia a third time     
        report_in = deepcopy(out)
        
        transit_depth = np.interp(report_in['1d']['extracted_flux'][0],
                                    both_spec['wave'], both_spec['frac'])
        report_in['1d']['extracted_flux'][1] = report_in['1d']['extracted_flux'][1]*transit_depth
    else: 
        #only run pandeia a third time if doing slope method and need accurate run for the 
        #nint and timing
        pandeia_input['configuration']['detector']['ngroup'] = timing['APT: Num Groups per Integration']
        pandeia_input['configuration']['detector']['nint'] = timing['Num Integrations In Transit']
        pandeia_input['configuration']['detector']['nexp'] = 1
  
        in_transit_spec = np.array([both_spec['wave'], both_spec['flux_in_trans']])
    
        pandeia_input['scene'][0]['spectrum']['sed']['spectrum'] = in_transit_spec

        report_in = perform_calculation(pandeia_input, dict_report=True)
        instrument = pandeia_input['configuration']['instrument']['instrument']
        #remove QY effects 
        report_in = remove_QY(report_in, instrument)
        report_in.pop('3d')
    
    return report_in
Beispiel #5
0
def compute_maxexptime_per_int(pandeia_input, sat_level):
    """Computes optimal maximum exposure time per integration
    
    Function to simulate 2d jwst image with 2 groups, 1 integration, 1 exposure 
    and return the maximum time 
    for one integration before saturation occurs. If saturation has 
    already occured, returns maxexptime_per_int as np.nan. This then 
    tells Pandexo to set min number of groups (ngroups =2). This avoids 
    error if saturation occurs. This routine assumes that min ngroups is 2. 
    
    Parameters
    ----------
    pandeia_input : dict 
        pandeia dictionary input 
    sat_level : int or float
        user defined saturation level in units of electrons
    
    Returns
    ------- 
    float 
        Maximum exposure time per integration before specified saturation level
    
    Examples
    --------
    
    >>> max_time = compute_maxexptime_per_int(pandeia_input, 50000.0)
    >>> print(max_time)
    12.0
    """
    
    #run once to get 2d rate image 
    pandeia_input['configuration']['detector']['ngroup'] = 2 
    pandeia_input['configuration']['detector']['nint'] = 1 
    pandeia_input['configuration']['detector']['nexp'] = 1
    
    report = perform_calculation(pandeia_input, dict_report=False)
    report_dict = report.as_dict() 
    
    # count rate on the detector in e-/second/pixel
    #det = report_dict['2d']['detector']
    det = report.signal.rate_plus_bg_list[0]['fp_pix']

    timeinfo = report_dict['information']['exposure_specification']
    #totaltime = timeinfo['tgroup']*timeinfo['ngroup']*timeinfo['nint']
    
    maxdetvalue = np.max(det)


    #maximum time before saturation per integration 
    #based on user specified saturation level

    try:
        maxexptime_per_int = sat_level/maxdetvalue
    except: 
        maxexptime_per_int = np.nan
    
    return maxexptime_per_int
    def run_calc(self, b):
        c = build_default_calc("wfirst", "wfirstimager", "imaging")
        c['configuration']['detector']['nexp'] = self.nexps.value
        c['configuration']['detector']['ngroup'] = self.ngroups.value
        c['configuration']['detector']['nint'] = self.nints.value
        c['configuration']['detector']['readmode'] = self.readmode.value
        c['configuration']['detector']['subarray'] = self.subarray.value
        c['configuration']['instrument']['filter'] = self.filt.value

        src = c['scene'][0]
        if self.src_select.value == "extended":
            src['shape']['geometry'] = 'sersic'
            a = self.ext_scale.value
            e = self.ellip.value
            b = (1.0 - e) * a
            s_idx = self.sersic_idx[self.sersic.value]
            # if gaussian, convert a/b to sigma
            if s_idx == 0.5:
                a *= np.sqrt(2.0)
                b *= np.sqrt(2.0)
            src['shape']['major'] = a
            src['shape']['minor'] = b
            src['shape']['sersic_index'] = s_idx
            src['position']['orientation'] = self.posang.value

        src['spectrum']['redshift'] = self.redshift.value
        src['spectrum']['normalization']['norm_flux'] = self.flux.value
        src['spectrum']['normalization']['norm_fluxunit'] = self.units.value
        src['spectrum']['normalization']['norm_wave'] = self.wave.value

        sed = self.sed_select.value
        if sed == "power-law":
            src['spectrum']['sed']['sed_type'] = "powerlaw"
            src['spectrum']['sed']['index'] = self.pl_index.value
        if sed == "blackbody":
            src['spectrum']['sed']['sed_type'] = "blackbody"
            src['spectrum']['sed']['temp'] = self.bb_temp.value
        if sed == "star":
            src['spectrum']['sed']['sed_type'] = "phoenix"
            src['spectrum']['sed']['key'] = self.star_config[self.stars.value]
        if sed == "extragalactic":
            src['spectrum']['sed']['sed_type'] = "brown"
            src['spectrum']['sed']['key'] = self.gal_config[self.galaxies.value]

        c['strategy']['aperture_size'] = self.ap_size.value
        ann = [self.ann_inner.value, self.ann_outer.value]
        c['strategy']['sky_annulus'] = ann

        self.r = perform_calculation(c, dict_report=True)
        self.calc_input = c
        self.plot_form.visible = True
        self.esn.value = "%.2f" % self.r['scalar']['sn']
        self.eflux.value = "%.2f" % self.r['scalar']['flux']
        self.etime.value = "%.2f" % self.r['scalar']['on_source_time']
        self.tab_form.visible = True

        self.update_plots()
Beispiel #7
0
def compute_maxexptime_per_int(pandeia_input, sat_level):
    """Computes optimal maximum exposure time per integration
    
    Function to simulate 2d jwst image with 2 groups, 1 integration, 1 exposure 
    and return the maximum time 
    for one integration before saturation occurs. If saturation has 
    already occured, returns maxexptime_per_int as np.nan. This then 
    tells Pandexo to set min number of groups (ngroups =2). This avoids 
    error if saturation occurs. This routine assumes that min ngroups is 2. 
    
    Parameters
    ----------
    pandeia_input : dict 
        pandeia dictionary input 
    sat_level : int or float
        user defined saturation level in units of electrons
    
    Returns
    ------- 
    float 
        Maximum exposure time per integration before specified saturation level
    
    Examples
    --------
    
    >>> max_time = compute_maxexptime_per_int(pandeia_input, 50000.0)
    >>> print(max_time)
    12.0
    """
    
    #run once to get 2d rate image 
    pandeia_input['configuration']['detector']['ngroup'] = 2 
    pandeia_input['configuration']['detector']['nint'] = 1 
    pandeia_input['configuration']['detector']['nexp'] = 1
    
    report = perform_calculation(pandeia_input, dict_report=False)
    report_dict = report.as_dict() 

    
    # count rate on the detector in e-/second 
    det = report_dict['2d']['detector']
    
    timeinfo = report_dict['information']['exposure_specification']
    #totaltime = timeinfo['tgroup']*timeinfo['ngroup']*timeinfo['nint']
    
    maxdetvalue = np.max(det)
    #maximum time before saturation per integration 
    #based on user specified saturation level
    try:
        maxexptime_per_int = sat_level/maxdetvalue
    except: 
        maxexptime_per_int = np.nan
    
    return maxexptime_per_int
Beispiel #8
0
def target_acq(instrument, both_spec, warning):
    """Contains functionality to compute optimal TA strategy 

    Takes pandexo normalized flux from create_input and checks for saturation, or 
    if SNR is below the minimum requirement for each. Then adds warnings and 2d displays 
    and target acq info to final output dict 

    Parameters
    ----------
    instrument : str 
        possible options are niriss, nirspec, miri and nircam 
    both_spec : dict
        output dictionary from **create_input** 
    warning : dict 
        output dictionary from **add_warnings** 

    Retruns
    -------

    """
    out_spectrum = np.array([both_spec['wave'], both_spec['flux_out_trans']])

    #this automatically builds a default calculation
    #I got reasonable answers for everything so all you should need to do here is swap out (instrument = 'niriss', 'nirspec','miri' or 'nircam')
    c = build_default_calc(telescope='jwst',
                           instrument=instrument,
                           mode='target_acq',
                           method='taphot')
    c['scene'][0]['spectrum']['sed'] = {
        'sed_type': 'input',
        'spectrum': out_spectrum
    }
    c['scene'][0]['spectrum']['normalization']['type'] = 'none'
    rphot = perform_calculation(c, dict_report=True)

    #check warnings (pandeia doesn't return values for these warnings, so try will fail if all good)
    try:
        warnings['TA Satruated?'] = rphot['warnings']['saturated']
    except:
        warnings['TA Satruated?'] = 'All good'

    try:
        warnings['TA SNR Threshold'] = rphot['warnings']['ta_snr_threshold']
    except:
        warnings['TA SNR Threshold'] = 'All good'

    #build TA dict
    ta = {
        'sn': rphot['scalar']['sn'],
        'ngroup': rphot['input']['configuration']['detector']['ngroup'],
        'saturation': rphot['2d']['saturation']
    }
Beispiel #9
0
    def run_engine(self):
        c = build_default_calc("wfirst", "wfirstimager", "imaging")
        c['configuration']['detector']['nexp'] = self.nexps.value
        c['configuration']['detector']['ngroup'] = self.ngroups.value
        c['configuration']['detector']['nint'] = self.nints.value
        c['configuration']['detector']['readmode'] = self.readmode.value
        c['configuration']['detector']['subarray'] = self.subarray.value
        c['configuration']['instrument']['filter'] = self.filt.value

        src = c['scene'][0]
        if self.src_select.value == "extended":
            src['shape']['geometry'] = 'sersic'
            a = self.ext_scale.value
            e = self.ellip.value
            b = (1.0 - e) * a
            s_idx = self.sersic_idx[self.sersic.value]
            # if gaussian, convert a/b to sigma
            if s_idx == 0.5:
                a *= np.sqrt(2.0)
                b *= np.sqrt(2.0)
            src['shape']['major'] = a
            src['shape']['minor'] = b
            src['shape']['sersic_index'] = s_idx
            src['position']['orientation'] = self.posang.value

        src['spectrum']['redshift'] = self.redshift.value
        src['spectrum']['normalization']['norm_flux'] = self.flux.value
        src['spectrum']['normalization']['norm_fluxunit'] = self.units.value
        src['spectrum']['normalization']['norm_wave'] = self.wave.value

        sed = self.sed_select.value
        if sed == "power-law":
            src['spectrum']['sed']['sed_type'] = "powerlaw"
            src['spectrum']['sed']['index'] = self.pl_index.value
        if sed == "blackbody":
            src['spectrum']['sed']['sed_type'] = "blackbody"
            src['spectrum']['sed']['temp'] = self.bb_temp.value
        if sed == "star":
            src['spectrum']['sed']['sed_type'] = "phoenix"
            src['spectrum']['sed']['key'] = self.star_config[self.stars.value]
        if sed == "extragalactic":
            src['spectrum']['sed']['sed_type'] = "brown"
            src['spectrum']['sed']['key'] = self.gal_config[self.galaxies.value]

        c['strategy']['aperture_size'] = self.ap_size.value
        c['strategy']['sky_annulus'] = self.background_annulus.value

        self._calculation_result = perform_calculation(c, dict_report=True)
        self.calculation_input = c
            config['strategy']['aperture_size'] = 0.063 * 2.5

        # --- iteratively find the flux yielding a S/N=10

        target_SN = 10.
        min_flux = 5.
        max_flux = 70.
        tol = 0.01

        while min_flux <= max_flux:

            current_flux = (min_flux + max_flux) / 2.

            scene['spectrum']['normalization']['norm_flux'] = current_flux
            config['scene'] = [scene]
            report = perform_calculation(config)

            sn = report['scalar']['sn']

            if sn <= target_SN:
                min_flux = current_flux + tol
            else:
                max_flux = current_flux - tol

        limit = current_flux

        print(f, '{0:.2f}'.format(limit))

        limits[f] = limit

    pickle.dump(limits, open('NIRCam_limits.p', 'wb'))
imgr_data['scene'][0]['spectrum']['normalization']['norm_flux'] = mag = 29
imgr_data['scene'][0]['spectrum']['normalization']['norm_fluxunit'] = 'abmag'
imgr_data['configuration']['detector']['nexp'] = nexp = 4
imgr_data['configuration']['detector']['nint'] = nint = 1
imgr_data['configuration']['detector']['ngroup'] = ngroup = 5
imgr_data['configuration']['detector']['readmode'] = readmode = 'bright1'
imgr_data['configuration']['instrument']['filter'] = filt = 'f200w'
imgr_data['background'] = background

ch = 'sw'
imgr_data['configuration']['instrument']['aperture'] = ch
imgr_data['configuration']['instrument']['mode'] = ch + '_imaging'
imgr_data['strategy']['aperture_size'] = 0.08  # radius (default 0.1")
imgr_data['strategy']['sky_annulus'] = 0.6, 0.99  # (default 0.22" - 0.4")

results = perform_calculation(imgr_data)
exptime = results['scalar']['exposure_time']

line = readmode.ljust(10)
snr = results['scalar']['sn']
line += '%2d  %2d  %2d  %7.2f  %12.8f' % (ngroup, nint, nexp, exptime, snr)
print line
print "Initial test complete..."
print

######

outdir = 'results'
if not os.path.exists(outdir):
    os.mkdir(outdir)
def calc_limits(configs,
                apertures,
                fluxes,
                scanfac=10,
                obsmode=None,
                exp_config=None,
                exp_configs=None,
                strategy=None,
                nflx=40,
                background='miri',
                skyfacs=None,
                orders=None,
                lim_snr=10.0):
    """
    Script to calculate sensitivity limits for extended sources using Pandeia. The script calculates a
    grid of models by varying the source flux density, yielding a numerical function SNR(F).
    The sensitivity limit is then calculated by inverting the function SNR(F) => F(SNR=lim_snr). It is possible
    to calculate the limiting sensitivity for a single Pandeia configuration, or for a list of N configurations,
    with some limitations. Most often, the user will probably loop over a list of N configurations, varying a filter,
    grating or other parameter.

    Parameters
    ----------
    configs: list of N dicts
        Contains dictionaries containing keyword/value pairs relevant for a Pandeia obsmode.
        For a given mode, passed values will be replaced, while others will default. These are the values
        of the configuration that are VARIED. For instance, the user might want to loop over different filter values.
    apertures: list of N floats
        Extraction apertures for each config.
    fluxes: List of N floats
        Initial guess of limiting flux in mJy
    scanfac: float
        Factor within which to search for the limiting flux. IMPORTANT: It is currently the users
        responsibility to check that the limiting flux is contained within fluxes/scanfac -> fluxes*scanfac.
        for spectroscopic modes, this should be true for every wavelength.
    obsmode: dict
        These are dictionary keyword/value pairs of a Pandeia obsmode that are NOT varied. Typically,
        this would identify the instrument and mode.
    exp_config: dict
        This is a dictionary of non-default values for a Pandeia exposure configuration.
        Set EITHER exp_config OR exp_configs.
    exp_configs: list of N dicts
        The user may want to use a different set of exposure configs for every obsmode config.
        In that case use this to pass N different versions.
    strategy: dict
        A dictionary containing a valid Pandeia strategy. It is currently not possible to change the
        strategy for every configuration.
    nflx: Integer
        Number of flux values in the grid. 10 is a reasonable default if the limit guess is close.
    background: String
        Invoke a canned background. There are various option, but most people will want to set this to "minzodi12".
        It is currently not possible to pass a new background beyond editing this scripts, but but it would not be
        difficult to implement.
    skyfacs: float
        If the strategy is set to use background subtraction, this sets the size of the sky extraction aperture relative to the
        extraction aperture.
    orders: List of N integers
        This sets the order for each configuration. This is the only strategy parameter that can currently be varied.
    lim_snr: float
        Limiting signal-to-noise ratio. The standard (and default) is 10.0.


    Returns
    ----------
    Dictionary containing:
        configs: List of the input Pandeia configurations
        strategy:  List of the input Pandeia strategies
        wavelengths: The effective wavelength for imaging modes and wavelength arrays for spectroscopic modes
        sns: The signal-to-noise ratio for each configuration. This is for debugging purposes.
        lim_fluxes: The calculated limiting flux densities in mJy/extraction aperture size.
        source_rates_per_njy: Detected electron rates for a source of 1 nJy.
        sat_limits: The calculated saturation limit
        orders: The input spectral order
        line_limits: Limiting integrated line flux in W/m^2/extraction aperture size.

    """

    if background is 'miri':
        syspath = os.path.abspath(os.path.dirname(__file__))
        bg_table = ascii.read(
            os.path.join(syspath, 'inputs/miri_verification_background.tab'))
        # From the matlab doc, it appears that the thermal background is not subjected to the OTE transmission
        background = [bg_table['col1'].data, bg_table['col2'].data]
    elif background in ['nirspec']:
        syspath = os.path.abspath(os.path.dirname(__file__))
        bg_table = fits.getdata(
            os.path.join(syspath,
                         'inputs/nirspec_verification_background.fits'))
        background = [bg_table['wavelength'], bg_table['intensity']]
    elif background in ['nircam']:
        syspath = os.path.abspath(os.path.dirname(__file__))
        bg_table = ascii.read(
            os.path.join(syspath, 'inputs/nircam_verification_background.tab'))
        background = [bg_table['col1'].data, bg_table['col2'].data]
    elif background in ['nircam_061416']:
        syspath = os.path.abspath(os.path.dirname(__file__))
        bg_table = ascii.read(
            os.path.join(
                syspath,
                'inputs/nircam_verification_background_mrieke_061416.tab'))
        wave_mu = bg_table['col1'].data
        flambda = bg_table['col2'].data
        scat_MJy = bg_table['col3'].data
        c_mu = con.c * 1e6
        fnu = (wave_mu**2 / c_mu) * flambda
        fnu_Jy = fnu * 1e26
        fnu_MJy = fnu_Jy / 1e6
        background = [wave_mu, fnu_MJy + scat_MJy]
    elif background in ['niriss']:
        syspath = os.path.abspath(os.path.dirname(__file__))
        bg_table = ascii.read(
            os.path.join(syspath, 'inputs/NIRISS_background.txt'))
        background = [bg_table['col1'].data, bg_table['col2'].data]
    elif background in ['zodi_only']:
        syspath = os.path.abspath(os.path.dirname(__file__))
        bg_table = ascii.read(os.path.join(syspath,
                                           'inputs/zodi_standard.txt'))
        background = [bg_table['col1'].data, bg_table['col2'].data]
    elif background in ['minzodi12']:
        syspath = os.path.abspath(os.path.dirname(__file__))
        bg_table = fits.getdata(
            os.path.join(syspath, 'inputs/minzodi12_12052016.fits'))
        background = [bg_table['wavelength'], bg_table['background']]

    source = {
        'id': 1,
        'target': True,
        'position': {
            'orientation': 23.0,
            'ang_unit': 'arcsec',
            'x_offset': 0.0,
            'y_offset': 0.0,
        },
        'shape': {
            'geometry': 'flat',
            'norm_method': 'surf_center',
            'surf_area_units': 'arcsec^2',
            'major': 0.9,
            'minor': 0.9
        },
        'spectrum': {
            'normalization': {
                'type': 'at_lambda',
                'norm_waveunit': 'microns',
                'norm_fluxunit': 'mjy',
                'norm_flux': 0.1,
                'norm_wave': 2.
            },
            'sed': {
                'sed_type': 'input',
                'wmin': 0.4,
                'wmax': 30.,
                'sed_type': 'flat',
            },
            'lines': []
        }
    }

    wavelengths = []
    sns = []
    lim_fluxes = []
    sat_limits = []
    line_limits = []
    source_rates = []
    for i in range(len(configs)):
        config = configs[i]
        aperture = apertures[i]
        flux = fluxes[i]

        if np.size(exp_configs) > 1:
            exp_config = exp_configs[i]

        if np.size(skyfacs) > 1:
            skyfac = skyfacs[i]
        else:
            skyfac = skyfacs

        strategy['aperture_size'] = aperture
        if orders is not None:
            strategy['order'] = orders[i]

        if skyfacs is None:
            inner_fac = 2.
            outer_fac = 5.
        else:
            if obsmode['mode'] in [
                    'msa', 'fixed_slit', 'lrsslit', 'lrsslitless', 'wfgrism',
                    'ssgrism', 'wfss', 'soss'
            ]:
                inner_fac = 1.5
                outer_fac = inner_fac + skyfac / 2.
            elif obsmode['mode'] in [
                    'ifu', 'mrs', 'sw_imaging', 'lw_imaging', 'imaging', 'ami'
            ]:
                inner_fac = 2.
                outer_fac = np.sqrt(skyfac + inner_fac**2.)

        if 'sky_annulus' in strategy.keys():
            strategy['sky_annulus'] = [
                aperture * inner_fac, aperture * outer_fac
            ]

        for key in config.keys():
            obsmode[key] = config[key]

        scene = [source]

        flx_expansion = np.logspace(np.log10(flux / scanfac),
                                    np.log10(flux * scanfac), nflx)

        sn_arr = []

        for flux in flx_expansion:

            source['spectrum']['normalization']['norm_flux'] = flux

            input = {
                'scene': scene,
                'background': background,
                'configuration': {
                    'instrument': obsmode,
                    'detector': exp_config
                },
                'strategy': strategy
            }

            report = perform_calculation(input, dict_report=False)
            bg_pix_rate = np.min(report.bg_pix)
            aperture_source_rate = report.curves['extracted_flux'][1]
            aperture_bg_rate = report.curves['extracted_flux_plus_bg'][1][
                0] - aperture_source_rate[0]
            fov_source_rate = report.curves['total_flux'][1]
            fits_dict = report.as_fits()

            sn_arr.append(fits_dict['1d']['sn'][0].data['sn'])

            wavelength = fits_dict['1d']['sn'][0].data['wavelength']

        bsubs = np.isinf(fits_dict['1d']['sn'][0].data['sn'])

        sn_arr = np.array(sn_arr)
        lim_flx = np.array([
            np.interp(lim_snr, sn_arr[:, i], flx_expansion)
            for i in np.arange(wavelength.size)
        ])
        # Make sure that undefined points remain undefined in the limiting flux
        lim_flx[bsubs] = np.nan

        if len(lim_flx) > 0:
            lim_flx = lim_flx.flatten()

        wavelengths.append(wavelength)
        sns.append(fits_dict['1d']['sn'][0].data['sn'])
        lim_fluxes.append(lim_flx)
        source_rates.append(aperture_source_rate / flux / 1e6)

        nwaves = np.size(aperture_source_rate)

        midpoint = int(nwaves / 2)

        #The best one at the midpoint. It doesn't really matter what the reference spectrum is here. We
        #just need one to calculate the rate per mJy for the saturation estimate.
        source['spectrum']['normalization']['norm_flux'] = lim_flx[midpoint]

        input = {
            'scene': [source],
            'background': background,
            'configuration': {
                'instrument': obsmode,
                'detector': exp_config
            },
            'strategy': strategy
        }

        report = perform_calculation(input, dict_report=False)
        fits_dict = report.as_fits()
        res_dict = report.as_dict()

        aperture_source_rate = report.curves['extracted_flux'][1]
        aperture_total_rate = report.curves['extracted_flux_plus_bg'][1]
        aperture_bg_rate = aperture_total_rate - aperture_source_rate
        fov_source_rate = report.curves['total_flux'][1]

        tgroup = report.signal.current_instrument.get_exposure_pars().tgroup
        tframe = report.signal.current_instrument.get_exposure_pars().tframe
        tfffr = report.signal.current_instrument.get_exposure_pars().tfffr
        det_type = report.signal.current_instrument.get_exposure_pars(
        ).det_type

        #nprerej =  report.signal.current_instrument.get_exposure_pars().nprerej

        if det_type == 'h2rg':
            mintime = tfffr + 2 * tframe
        else:
            mintime = tfffr + 5 * tframe  #minimum recommended frames is 5 for MIRI

        # Soss is rotated by 90 degrees on the detector, for some reason
        if obsmode['mode'] is 'soss':
            report.bg_pix = np.rot90(report.bg_pix)
            report.signal.rate = np.rot90(report.signal.rate)

        # Spectrum?
        if (lim_flx.shape[0] > 1) and (report.signal.rate.shape[1] !=
                                       lim_flx.shape[0]):
            #slitless modes have excess pixels on each side - excess should always be an odd integer
            excess = (report.signal.rate.shape[1] - lim_flx.shape[0]) - 1
        else:
            excess = 0

        if excess == 0:
            fullwell_minus_bg = (report.signal.det_pars['fullwell'] -
                                 mintime * report.bg_pix)
            rate_per_mjy = report.signal.rate / lim_flx[midpoint]
            bg_pix_rate_min = np.min(report.bg_pix, 0)
            bg_pix_rate_max = np.max(report.bg_pix, 0)
        else:
            bg_pix_rate_min = np.min(report.bg_pix[:,
                                                   int(excess /
                                                       2):-int(excess / 2)])
            bg_pix_rate_max = np.max(report.bg_pix[:,
                                                   int(excess /
                                                       2):-int(excess / 2)])
            fullwell_minus_bg = (
                report.signal.det_pars['fullwell'] - mintime *
                report.bg_pix[:, int(excess / 2):-int(excess / 2) - 1])
            rate_per_mjy = report.signal.rate[:,
                                              int(excess /
                                                  2):-int(excess / 2) -
                                              1] / lim_flx[midpoint]

        sat_limit_detector = fullwell_minus_bg / mintime / np.abs(
            rate_per_mjy)  #units of mJy

        # Calculate line sensitivities, assuming unresolved lines.
        if lim_flx.shape[0] > 1:
            sat_limit = np.min(sat_limit_detector, 0)
            r = report.signal.current_instrument.get_resolving_power(
                wavelength)
            px_width_micron = np.abs(wavelength - np.roll(wavelength, 1))
            px_width_micron[:1] = px_width_micron[1]

            freqs = 2.99792458e14 / wavelength
            px_width_hz = np.abs(freqs - np.roll(freqs, 1))
            px_width_hz[:1] = px_width_hz[1]

            line_width_px = wavelength / r / px_width_micron
            line_limit = lim_flx * 1e-3 * 1e-26 * px_width_hz * line_width_px / np.sqrt(
                line_width_px)
            line_limits.append(line_limit)
        else:
            sat_limit = np.min(sat_limit_detector)

        sat_limits.append(sat_limit)

        print('Configuration:', config)
        print('Exposure Time:',
              '{:7.2f}'.format(res_dict['scalar']['exposure_time']))
        print('Total Exposure Time:',
              '{:7.2f}'.format(res_dict['scalar']['total_exposure_time']))
        print('Extracted flux/nJy:',
              aperture_source_rate[midpoint] / lim_flx[midpoint] / 1e6)
        print('Saturation limit [mJy]', '{:7.2f}'.format(np.min(sat_limit)))
        print('Extracted source in e-/s:',
              '{:7.2f}'.format(aperture_source_rate[midpoint]))
        print('Extracted flux plus background in e-/s:',
              '{:7.2f}'.format(aperture_total_rate[midpoint]))
        print(
            'Encircled energy:', '{:7.2f}'.format(
                100 * aperture_source_rate[midpoint] /
                fov_source_rate[int(fov_source_rate.shape[0] / 2)]) + '%')
        print('Background rate per pix (min and max):',
              '{:7.2f}'.format(np.min(bg_pix_rate_min)),
              '{:7.2f}'.format(np.max(bg_pix_rate_max)))
        print('Aperture radius:', aperture, 'arcseconds')
        print('Extraction area [sq. pixels]:', report.extraction_area)
        print('Background area [sq. pixels]:', report.background_area)
        print('Limiting flux:', '{:7.2e}'.format(np.min(lim_flx)), 'mJy')
        print('SNR:',
              '{:7.2f}'.format(fits_dict['1d']['sn'][0].data['sn'][midpoint]))
        print(
            'Reference wavelength:', '{:7.2e}'.format(
                fits_dict['1d']['sn'][0].data['wavelength'][midpoint]))

    return {
        'configs': configs,
        'strategy': strategy,
        'wavelengths': wavelengths,
        'sns': sns,
        'lim_fluxes': lim_fluxes,
        'source_rates_per_njy': source_rates,
        'sat_limits': sat_limits,
        'orders': orders,
        'line_limits': line_limits
    }
Beispiel #13
0
                        'background', 'extinction'):
        shutil.copytree(join(pandeia_refdata, folder_name),
                        join(destination, folder_name))

    # Patch the data package's config file for WFIRST WFI
    shutil.copy(
        join(dirname(__file__), 'data_patch', 'wfirstimager_config.json'),
        join(destination, 'wfirst', 'wfirstimager', 'config.json'))

    log("New data package in", destination)
    return destination


if __name__ == "__main__":
    # Make package
    data_dir = make_data_package(log=print, overwrite=True)

    # Test package
    os.environ['pandeia_refdata'] = data_dir
    from pandeia.engine.perform_calculation import perform_calculation
    from pandeia.engine.calc_utils import build_default_calc
    calc = build_default_calc("wfirst", "wfirstimager", "imaging")
    result = perform_calculation(calc, dict_report=True)
    print("Successfully performed the default calculation for wfirstimager")
    if sys.version_info > (3, 2):
        archive_name = shutil.make_archive('pandeia_wfirst_data',
                                           'gztar',
                                           root_dir=dirname(data_dir),
                                           base_dir=basename(data_dir))
        print("Compressed WFIRST Pandeia data into {}".format(archive_name))
Beispiel #14
0
def sn_user_spec(inputs, disperser = 'prism', filt = 'clear', ngroup = 19, nint = 2, nexp = 36):
    wl = inputs['wl'] #in microns
    spec = inputs['spec'] #in mJy
    xoff = inputs['xoff']
    yoff = inputs['yoff']
    
    configuration=build_default_calc('jwst','nirspec','msa')
    configuration['configuration']['instrument']['disperser']=disperser
    configuration['configuration']['instrument']['filter']=filt
    #E - I *think* shutter location is just so that the detector gap is placed in the correct place
    configuration['configuration']['instrument']['shutter_location']='q3_345_20'#'q4_345_20'
    #exposure specifications from DEEP APT file
    configuration['configuration']['detector']['ngroup']=ngroup
    configuration['configuration']['detector']['nint']=nint
    #PRISM
    configuration['configuration']['detector']['nexp']=nexp
    configuration['configuration']['detector']['readmode']='nrsirs2'
    configuration['strategy']['dithers'][0]['on_source'] = inputs['onSource']
    configuration['configuration']['instrument']['slitlet_shape'] = inputs['slitletShape']
    
    #default configuration['configuration'] has 1x3 shutter config
    scene = {}
    if (inputs['re_circ'] > 0):
        sersic=inputs['sersic_n']
        re = inputs['re_maj']
        ellip=1.-inputs['axis_ratio']
        pa=inputs['position_angle']
        major_axis = re
        minor_axis = re*inputs['axis_ratio']
        #pandeia used to use scale lengths, but now is fine with half-light radii
        scene['shape']={'geometry':'sersic','major': major_axis,'minor':minor_axis,'sersic_index':sersic}
#        #pandiea wants scale lengths not half-light radii
#        major_axis,minor_axis=majorminor(sersic,rc,ellip)
#        scene['position'] = {'x_offset':xoff, 'y_offset': yoff, 'orientation': pa, 'position_parameters':['x_offset','y_offset','orientation']}
#        scene['shape']={'geometry':'sersic','major': major_axis,'minor':minor_axis,'sersic_index':sersic}
#        print scene['shape']
    else:
        pa=inputs['position_angle']
        #this is the dummy trigger to go for a point source
        scene['position'] = {'x_offset':xoff, 'y_offset': yoff, 'orientation': pa, 'position_parameters':['x_offset','y_offset','orientation']}
        scene['shape']={'geometry':'point'}
    
    scene['spectrum'] = {}
    scene['spectrum']['name'] = "continuum_spectrum"
    scene['spectrum']['redshift'] = 0 #because otherwise it shifts the wavelength array...
    #it doesn't seem to do anything with the normalization of the
    #source spectrum, however?!
    tempIdx = np.where((wl >= filterWlDict[filt]['low']) & (wl <= filterWlDict[filt]['high']))[0]
    scene['spectrum']['sed'] = {'sed_type': 'input', 'spectrum': [wl[tempIdx], spec[tempIdx]], 'unit':'mJy'}
    scene['spectrum']['normalization'] = {}
    scene['spectrum']['normalization'] = {'type': 'none'}
    
    if args.addLines:
      emission_line_array = []
      for i,f in enumerate(inputs['flux']):
        flux=f
        wave=inputs['wave'][i]
        if wave > filterWlDict[filt]['low'] and wave < filterWlDict[filt]['high']:
          emission_line={}
          emission_line['emission_or_absorption']='emission'
          emission_line['center']=wave
          #TODO: check units...
          emission_line['strength']=flux
          emission_line['profile']='gaussian'
          #assume the line is basically unresolved, i.e. 50 km/s (FWHM)
          emission_line['width']=50.#50.
          emission_line_array.append(emission_line)
      scene['spectrum']['lines']=emission_line_array

    configuration['scene'][0]=scene
    report=perform_calculation(configuration)
    return report
Beispiel #15
0
    def run_calc(self, b):
        # notify the user that we're calculating.
        self.computing_notice.layout.display = 'inline'
        calc_mode = self.mode_select.value.lower()
        calc_strat = self.strat_select[calc_mode].value.lower()
        c = build_default_calc("wfirst",
                               "wfirstimager",
                               calc_mode,
                               method=calc_strat)
        c['configuration']['detector']['nexp'] = self.mode_config[
            calc_mode].nexps.value
        c['configuration']['detector']['ngroup'] = self.mode_config[
            calc_mode].ngroups.value
        c['configuration']['detector']['nint'] = self.mode_config[
            calc_mode].nints.value
        c['configuration']['detector']['readmode'] = self.mode_config[
            calc_mode].readmode.value
        c['configuration']['detector']['subarray'] = self.mode_config[
            calc_mode].subarray.value
        c['configuration']['instrument']['filter'] = self.mode_config[
            calc_mode].filt.value

        c['scene'] = []

        for source in self.sources:
            s = build_default_source(
                geometry=geom_mapping[source.src_select.value])
            if source.src_select.value == "Power":
                s['shape']['r_core'] = source.r_core.value
                s['shape']['power_index'] = source.power.value
            elif source.src_select.value == "Flat":
                s['shape']['major'] = source.major.value
                s['shape']['minor'] = source.minor.value
                s['shape']['norm_method'] = norm_mapping[
                    source.norm_flat.value]
                s['position']['orientation'] = source.pos_a.value
            elif source.src_select.value == "2D Gaussian":
                s['shape']['major'] = source.major.value
                s['shape']['minor'] = source.minor.value
                s['shape']['norm_method'] = norm_mapping[source.norm.value]
                s['position']['orientation'] = source.pos_a.value
            elif source.src_select.value == "Sersic (Effective Radius)":
                s['shape']['major'] = source.major.value
                s['shape']['minor'] = source.minor.value
                s['shape']['norm_method'] = norm_mapping[source.norm.value]
                s['shape']['sersic_index'] = source.sersic.value
                s['position']['orientation'] = source.pos_a.value
            elif source.src_select.value == "Sersic (Scale Radius)":
                s['shape']['major'] = source.major.value
                s['shape']['minor'] = source.minor.value
                s['shape']['norm_method'] = norm_mapping[source.norm.value]
                s['shape']['sersic_index'] = source.sersic.value
                s['position']['orientation'] = source.pos_a.value
            else:
                pass

            s['position']['x_offset'] = source.pos_x.value
            s['position']['y_offset'] = source.pos_y.value

            s['spectrum']['redshift'] = source.redshift.value
            s['spectrum']['normalization']['norm_flux'] = source.flux.value
            s['spectrum']['normalization'][
                'norm_fluxunit'] = source.funits.value
            s['spectrum']['normalization']['norm_wave'] = source.wave.value

            sed = source.sed_select.value
            if sed == "power-law":
                s['spectrum']['sed']['sed_type'] = "powerlaw"
                s['spectrum']['sed']['index'] = source.pl_index.value
            if sed == "blackbody":
                s['spectrum']['sed']['sed_type'] = "blackbody"
                s['spectrum']['sed']['temp'] = source.bb_temp.value
            if sed == "phoenix":
                s['spectrum']['sed']['sed_type'] = "phoenix"
                s['spectrum']['sed']['key'] = source.phoenix_config[
                    source.phoenix.value]
            if sed == "extragalactic":
                s['spectrum']['sed']['sed_type'] = "brown"
                s['spectrum']['sed']['key'] = source.gal_config[
                    source.galaxies.value]
            if sed == "star":
                s['spectrum']['sed']['sed_type'] = "hst_calspec"
                s['spectrum']['sed']['key'] = source.star_config[
                    source.star.value]

            c['scene'].append(s)

        c['strategy']['aperture_size'] = self.strat_config[
            calc_strat].ap_size.value
        ann = [
            self.strat_config[calc_strat].ann_inner.value,
            self.strat_config[calc_strat].ann_outer.value
        ]
        c['strategy']['sky_annulus'] = ann
        if calc_strat == 'specapphot':
            c['strategy']['reference_wavelength'] = self.strat_config[
                calc_strat].reference_wavelength.value

        self.r = perform_calculation(c, dict_report=True)
        self.calc_input = c
        self.esn.value = "%.2f" % self.r['scalar']['sn']
        self.eflux.value = "%.2f" % self.r['scalar']['extracted_flux']
        self.etime.value = "%.2f" % self.r['scalar']['total_exposure_time']

        self.update_plots()
        # Now set the result form to be shown
        self.computing_notice.layout.display = 'none'
        self.result_form.layout.display = 'inline'
def calc_backgrounds(configs,
                     obsmode=None,
                     exp_config=None,
                     strategy=None,
                     background='miri'):

    if background is 'miri':
        syspath = os.path.abspath(os.path.dirname(__file__))
        bg_table = ascii.read(
            os.path.join(syspath, 'inputs/miri_verification_background.tab'))
        # From the matlab doc, it appears that the thermal background is not subjected to the OTE transmission
        background = [bg_table['col1'].data, bg_table['col2'].data]
    elif background in ['nirspec']:
        syspath = os.path.abspath(os.path.dirname(__file__))
        bg_table = fits.getdata(
            os.path.join(syspath,
                         'inputs/nirspec_verification_background.fits'))
        background = [bg_table['wavelength'], bg_table['intensity']]
    elif background in ['nircam']:
        syspath = os.path.abspath(os.path.dirname(__file__))
        bg_table = ascii.read(
            os.path.join(syspath, 'inputs/nircam_verification_background.tab'))
        background = [bg_table['col1'].data, bg_table['col2'].data]
    elif background in ['niriss']:
        syspath = os.path.abspath(os.path.dirname(__file__))
        bg_table = ascii.read(
            os.path.join(syspath, 'inputs/nircam_verification_background.tab'))
        background = [bg_table['col1'].data, bg_table['col2'].data]

    source = {
        'id': 1,
        'target': True,
        'position': {
            'orientation': 23.0,
            'ang_unit': 'arcsec',
            'x_offset': 0.0,
            'y_offset': 0.0,
        },
        'shape': {
            'major': 0.0,
            'minor': 0.0
        },
        'spectrum': {
            'normalization': {
                'type': 'at_lambda',
                'norm_waveunit': 'microns',
                'norm_fluxunit': 'mjy',
                'norm_flux': 1e-10,
                'norm_wave': 2.
            },
            'sed': {
                'sed_type': 'input',
                'wmin': 0.4,
                'wmax': 30.,
                'sed_type': 'flat',
            },
            'lines': []
        }
    }

    bgs = []
    wavelengths = []
    for config in configs:

        for key in config.keys():
            obsmode[key] = config[key]

        scene = [source]

        input = {
            'scene': scene,
            'background': background,
            'configuration': {
                'instrument': obsmode,
                'detector': exp_config
            },
            'strategy': strategy
        }

        report = perform_calculation(input, dict_report=False)

        if report.bg_pix.shape[0] != report.bg_pix.shape[1]:
            bg_pix_rate = np.max(report.bg_pix, axis=0)
        else:
            bg_pix_rate = np.max(report.bg_pix)

        fits_dict = report.as_fits()

        wavelength = fits_dict['1d']['sn'][0].data['wavelength']

        print('configuration:', config)

        bgs.append(bg_pix_rate)
        wavelengths.append(wavelength)

    return {'wavelengths': wavelengths, 'backgrounds': bgs, 'configs': configs}