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
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
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()
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
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'] }
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 }
'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))
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
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}