def test_changing_npix(): ''' Test that using different npix will result in same PSF ''' # Create a NIRCam instance using the default npix=1024 nircam_1024 = webbpsf.NIRCam() nircam_1024.pupilopd = None # Set to none so I don't have to worry about making new OPDs psf_1024 = nircam_1024.calc_psf(oversample=2, nlambda=1, add_distortion=False) # Create a NIRCam instance using npix=2048 npix = 2048 nircam_2048 = webbpsf.NIRCam() nircam_2048.pupil = os.path.join(webbpsf.utils.get_webbpsf_data_path(), f'jwst_pupil_RevW_npix{npix}.fits.gz') nircam_2048.pupilopd = None # Set to none so I don't have to worry about making new OPDs psf_2048 = nircam_2048.calc_psf(oversample=2, nlambda=1, add_distortion=False) # Let's check individual pixel values, at least where the PSF is not too dim. # Check all pixels which have > 1e-6 of the total flux (we can safely ignore pixels with very low intensity) mask = psf_1024[0].data>1e-6 assert np.allclose(psf_1024[0].data[mask], psf_2048[0].data[mask], rtol=0.01), 'Pixel values differ by more than 1%' # Let's check that the total flux in the PSF does not change much. # (A small amount is acceptable and not surprising, since higher resolution improves the fidelity at which # we model light that is scattered by segment edges to very wide angles outside of the simulated PSF FOV) assert np.isclose(psf_1024[0].data.sum(), psf_2048[0].data.sum(), rtol=0.005), "PSF total flux should not change much" # Let's also check a derived property of the whole PSF: the FWHM. # The FWHM should be very close to identical for the two PSFs. assert np.isclose(webbpsf.measure_fwhm(psf_1024), webbpsf.measure_fwhm(psf_2048), rtol=0.0001), "PSF FWHM should not vary for different npix"
def nircam_nocoro(filter, Aber_WSS): """ Create PSF :param filter: :param Aber_WSS: :return: """ # Create NIRCam object nc = webbpsf.NIRCam() # Set filter nc.filter = filter # Adjust OTE with aberrations nc, ote = webbpsf.enable_adjustable_ote(nc) nc.include_si_wfe = False # set SI internal WFE to zero ote.reset() ote.zero() for i in range(nb_seg): seg = wss_segs[i].split('-')[0] ote._apply_hexikes_to_seg(seg, Aber_WSS[i, :]) # Calculate PSF psf_nc = nc.calc_psf(oversample=1, fov_pixels=int(im_size_e2e), nlambda=1) psf_webbpsf = psf_nc[1].data return psf_webbpsf
def parse_aperture(aperture_name): ''' Return [image mask, pupil mask, fov_pixels, trim_fov_pixels, pixelscale] ''' aperture_keys = ['mask210r','mask335r','mask430r','masklwb','maskswb', 'fqpm1065','fqpm1140','fqpm1550','lyot2300'] assert aperture_name in aperture_keys, \ 'Aperture {} not recognized! Must be one of {}'.format(aperture_name, aperture_keys) nc = webbpsf.NIRCam() miri = webbpsf.MIRI() aperture_dict = { 'mask210r' : ['MASK210R','CIRCLYOT', 101, None, nc._pixelscale_short], 'mask335r' : ['MASK335R','CIRCLYOT', 101, None, nc._pixelscale_long], 'mask430r' : ['MASK430R','CIRCLYOT', 101, None, nc._pixelscale_long], 'masklwb' : ['MASKLWB','WEDGELYOT', 351, 101, nc._pixelscale_long], 'maskswb' : ['MASKSWB','WEDGELYOT', 351, 101, nc._pixelscale_short], 'fqpm1065' : ['FQPM1065','MASKFQPM', 81, None, miri.pixelscale], 'fqpm1140' : ['FQPM1140','MASKFQPM', 81, None, miri.pixelscale], 'fqpm1550' : ['FQPM1550','MASKFQPM', 81, None, miri.pixelscale], 'lyot2300' : ['LYOT2300','MASKLYOT', 81, None, miri.pixelscale] } return aperture_dict[aperture_name]
def psf_suite(nw=30, wmin=0.5, wmax=6., instrument=wp.NIRCam(), outname='PSF', fov_arcsec=1.5, aperture='any'): nw = float(nw) waves = (np.arange(nw) / (nw - 1))**2. * (wmax - wmin) + wmin waves_m = waves * 1e-6 for wave in waves_m: longname = outname + '_{0:.4f}'.format(wave * 1e6) + '.fits' longname = longname.lower() if os.path.isfile(longname): os.remove(longname) instrument.calcPSF(oversample=2, fov_arcsec=fov_arcsec, monochromatic=wave, outfile=longname, clobber='true') # Add mode keyword to header hdulist = pf.open(longname, mode='update') prihdr = hdulist[0].header prihdr['APERTURE'] = ( aperture, 'The observing aperture within the instrument FOV') hdulist.flush() hdulist.close()
def nircam_coro(filter, fpm, ppm, Aber_WSS): """ -- Deprecated function still used in analytical PASTIS and some notebooks. -- Create NIRCam image with specified filter and coronagraph, and aberration input. :param filter: str, filter name :param fpm: focal plane mask :param ppm: pupil plane mask - Lyot stop :param Aber_WSS: list or array holding Zernike coefficients ordered in WSS convention and in METERS :return: """ # Set up NIRCam and coronagraph nc = webbpsf.NIRCam() nc.filter = filter nc.image_mask = fpm nc.pupil_mask = ppm # Adjust OTE with aberrations nc, ote = webbpsf.enable_adjustable_ote(nc) nc.include_si_wfe = False # set SI internal WFE to zero ote.reset() ote.zero() for i in range(NB_SEG): seg = WSS_SEGS[i].split('-')[0] ote._apply_hexikes_to_seg(seg, Aber_WSS[i,:]) # Calculate PSF psf_nc = nc.calc_psf(oversample=1, fov_pixels=int(IM_SIZE_E2E), nlambda=1) psf_webbpsf = psf_nc[1].data return psf_webbpsf
def __init__(self, filter, width, resampling_factor=1): """ :param f: tje filter of the form JWST.NIRCam.XXX or JWST.MIRI.XXX :param width: Width of the image along a single axis (this is approximate since images must be odd in dimension) :param resampling_factor: The integer amount of resampling done to increase resolution. (int) :param gaussFWHM: If a simple gaussian PSF is required the FWHM of that PSF in arcseconds. (float) """ inst = filter.split('.')[1] f = filter.split('.')[-1] if filter in FLARE.filters.NIRCam_l: self.resolution = 0.063 elif filter in FLARE.filters.NIRCam_s: self.resolution = 0.031 else: print('filter not found') # Ndim must odd for convolution with the PSF ini_Ndim = int(width / self.resolution) if ini_Ndim % 2 != 0: self.Ndim = int(width / self.resolution) else: self.Ndim = int(width / self.resolution) + 1 if inst == 'NIRCAM': nc = webbpsf.NIRCam() nc.filter = f self.PSF = nc.calc_psf(oversample=resampling_factor, fov_pixels=self.Ndim)[0].data # compute PSF
def nircam_nocoro(filter, Aber_WSS): """ -- Deprecated function still used in analytical PASTIS and some notebooks. -- :param filter: :param Aber_WSS: :return: """ # Create NIRCam object nc = webbpsf.NIRCam() # Set filter nc.filter = filter # Adjust OTE with aberrations nc, ote = webbpsf.enable_adjustable_ote(nc) nc.include_si_wfe = False # set SI internal WFE to zero ote.reset() ote.zero() for i in range(NB_SEG): seg = WSS_SEGS[i].split('-')[0] ote._apply_hexikes_to_seg(seg, Aber_WSS[i,:]) # Calculate PSF psf_nc = nc.calc_psf(oversample=1, fov_pixels=int(IM_SIZE_E2E), nlambda=1) psf_webbpsf = psf_nc[1].data return psf_webbpsf
def test_single_seg_psf(segmentid=1): """Test calculation of a single segment PSF, including options to remove piston/tip/tilt as used by MIRAGE """ nrc = webbpsf.NIRCam() nrc.filter = 'F212N' nrc, ote = webbpsf.enable_adjustable_ote(nrc) ote.zero(zero_original=True) segname = webbpsf.constants.SEGNAMES_WSS_ORDER[segmentid-1][0:2] ote.move_seg_local(segname, xtilt=1, piston=-1) pupil = webbpsf.webbpsf_core.one_segment_pupil(segmentid) ote.amplitude = pupil[0].data psf = nrc.calc_psf(nlambda=1) ote.remove_piston = True ote.update_opd() psf_rm_piston = nrc.calc_psf(nlambda=1) assert np.allclose(psf[0].data, psf_rm_piston[0].data), "Piston removal should not affect the overall PSF" assert np.allclose( webbpsf.measure_centroid(psf), webbpsf.measure_centroid(psf_rm_piston)), "centroid should not shift" ote.remove_piston_tip_tilt = True ote.update_opd() psf_rm_ptt = nrc.calc_psf(nlambda=1) assert not np.allclose(psf[0].data, psf_rm_ptt[0].data), "Piston/Tip/Tip removal should shift the overall PSF" assert np.abs(webbpsf.measure_centroid(psf)[0] - webbpsf.measure_centroid(psf_rm_ptt)[0]) > 40, "centroid should shift susbtantially with/without tip/tilt removal"
def test_move_sur(plot=False): """ Test we can move mirrors using Segment Update Requests """ import webbpsf import os import glob surdir = os.path.join(webbpsf.__path__[0], 'tests', 'surs') surs = glob.glob(surdir+'/*sur.xml') nrc = webbpsf.NIRCam() nrc.filter='F212N' nrc, ote = webbpsf.enable_adjustable_ote(nrc) ote.zero(zero_original=True) for s in surs: print("Testing "+s) ote.reset() ote.move_sur(s) # the coarse phasing SUR is a no-op after 3 groups; all others have some effect if 'coarse_phasing' not in s: assert not np.allclose(ote.segment_state, 0), "Expected some segments to be moved" ote.move_sur(s, reverse=True) assert np.allclose(ote.segment_state, 0), "Reversing moves didn't bring us back to zero" # Test every DOF on A1-1 and SM and check the OTE state updated accordingly s = glob.glob(surdir+'/example_alldof_A1-SM_sur.xml')[0] print("Testing "+s) ote.reset() ote.move_sur(s) assert np.allclose(ote.segment_state[0], [1, 2, 3, 4, 5, 6]) assert np.allclose(ote.segment_state[-1], [1, 2, 3, 4, 5, 0]) # Test moving one at a time. This test relies on specifics of what's in the image stacking SUR. s = glob.glob(surdir+'/example_image_stacking*sur.xml')[0] print("Testing moving one group at a time with "+s) ote.reset() sur = webbpsf.surs.SUR(s) ngroups = len(sur.groups) oldstate = ote.segment_state.copy() for igrp in range(1, ngroups+1): print("Group {} should move segment {}".format(igrp, 2*igrp+6)) ote.move_sur(s, group=igrp) movedsegs = np.abs((ote.segment_state - oldstate).sum(axis=1)) assert (movedsegs!=0).sum()==1, "Only expected one segment to move" whichmoved = np.argmax(movedsegs)+1 print ("Moved segment", whichmoved) assert whichmoved == 2*igrp+6, "An unexpected segment moved" oldstate = ote.segment_state.copy() if plot: psf = nrc.calc_psf(fov_pixels=256, add_distortion=False) plt.figure() ote.display_opd(title="After Group {}".format(igrp)) plt.figure() webbpsf.display_psf(psf, ext=1, title="After Group {}".format(igrp))
def get_psf( wave, instrument, aperture_name, source_offset=(0, 0), otf_options=None, full_aperture=None): #Make the instrument and determine the mode if instrument.upper() == 'NIRCAM': ins = webbpsf.NIRCam() # WebbPSF needs to know the filter to select the optimal # offset for the bar masks. The filter is not passed into # get_psf but is stored in the full aperture name in self._psfs if aperture_name in ['masklwb', 'maskswb']: # Everything after the aperture name is the filter name. full_aperture = self._psfs[0]['aperture_name'] fname = full_aperture[full_aperture.find(aperture_name) + len(aperture_name):] ins.filter = fname if wave > 2.5: # need to toggle to LW detector. ins.detector='A5' ins.pixelscale = ins._pixelscale_long elif instrument.upper() == 'MIRI': ins = webbpsf.MIRI() else: raise ValueError('Only NIRCam and MIRI are supported instruments!') image_mask, pupil_mask, fov_pixels, trim_fov_pixels, pix_scl = parse_aperture(aperture_name) ins.image_mask = image_mask ins.pupil_mask = pupil_mask # Apply any extra options if specified by the user: for key in options.on_the_fly_webbpsf_options: ins.options[key] = options.on_the_fly_webbpsf_options[key] if options.on_the_fly_webbpsf_opd is not None: ins.pupilopd = options.on_the_fly_webbpsf_opd #get offset ins.options['source_offset_r'] = source_offset[0] ins.options['source_offset_theta'] = source_offset[1] ins.options['output_mode'] = 'oversampled' ins.options['parity'] = 'odd' psf_result = calc_psf_and_center(ins, wave, source_offset[0], source_offset[1], 3, pix_scl, fov_pixels, trim_fov_pixels=trim_fov_pixels) pix_scl = psf_result[0].header['PIXELSCL'] upsamp = psf_result[0].header['OVERSAMP'] diff_limit = psf_result[0].header['DIFFLMT'] psf = psf_result[0].data psf = { 'int': psf, 'wave': wave, 'pix_scl': pix_scl, 'diff_limit': diff_limit, 'upsamp': upsamp, 'instrument': instrument, 'aperture_name': aperture_name, 'source_offset': source_offset } return psf
def create_dummy_psf_grid(): """Use webbpsf to create a griddedPSFModel object""" nrc = webbpsf.NIRCam() nrc.filter = 'F200W' nrc.detector = 'NRCB1' grid = nrc.psf_grid(num_psfs=1, all_detectors=False, oversample=1, fov_pixels=301, add_distortion=False, save=False) return grid
def setup_coro(filter, fpm, ppm): """ Set up a NIRCam coronagraph object. :param filter: str, filter name :param fpm: focal plane mask :param ppm: pupil plane mask - Lyot stop :return: """ nc = webbpsf.NIRCam() nc.filter = filter nc.image_mask = fpm nc.pupil_mask = ppm return nc
def generate_random_ote_deployment(out_dir, reduction_factor=0.2, save=True): """Create a WebbPSF adjustable OTE object representing a perturbed OTE mirror state by randomly generating mirror deployment errors. Parameters ---------- out_dir : str Path to directory in which to save OPD FITS files and the deployment error dictionary reduction_factor : optional Factor by which to reduce the input deployment errors. Default is 0.2. save : bool, optional Denotes whether to save out the OPD (with and without tip/tilt) as FITs files and the deployment error dictionary as a yaml Returns ------- ote : webbpsf.opds.OTE_Linear_Model_WSS object WebbPSF OTE object describing perturbed OTE state with tip and tilt removed segment_tilts : numpy.ndarray List of X and Y tilts for each mirror segment, in microradians ote_opd_with_tilts : numpy.ndarray Array representing perturbed OTE OPD pupil before tip/tilt is removed """ # Make an adjustable OTE object with WebbPSF nc = webbpsf.NIRCam() nc, ote = webbpsf.enable_adjustable_ote(nc) # Generate OPD and vector list with reduced deployment errors deployment_errors = generate_deployment_errors(out_dir=out_dir, save=save) deployment_errors = reduce_deployment_errors( deployment_errors, reduction_factor=reduction_factor, out_dir=out_dir, save=save) # Create an OTE object with those deployment errors ote, segment_tilts = apply_deployment_errors(ote, deployment_errors, out_dir=out_dir, save=save) ote_opd_with_tilts = ote.opd # Save array to plot below # Remove tip and tilt ote = remove_piston_tip_tilt(ote, out_dir=out_dir, save=save) return ote, segment_tilts, ote_opd_with_tilts
def set_up_nircam(): """ Return a configured instance of the NIRCam simulator on JWST. Sets up the Lyot stop and filter from the configfile, turns of science instrument (SI) internal WFE and zeros the OTE. :return: Tuple of NIRCam instance, and its OTE """ nircam = webbpsf.NIRCam() nircam.include_si_wfe = False nircam.filter = CONFIG_PASTIS.get('JWST', 'filter_name') nircam.pupil_mask = CONFIG_PASTIS.get('JWST', 'pupil_plane_stop') nircam, ote = webbpsf.enable_adjustable_ote(nircam) ote.zero(zero_original=True) # https://github.com/spacetelescope/webbpsf/blob/96537c459996f682ac6e9af808809ca13fb85e87/webbpsf/opds.py#L1125 return nircam, ote
def test_pupilopd_none(): """Test that setting pupilopd=None does in fact result in no WFE In particular, this tests that setting opd to None also results in disabling the field-dependent component of the OTE linear model, as well as setting the global WFE component to zero. """ nrc = webbpsf.NIRCam() nrc.pupilopd = None nrc.include_si_wfe = False ote_lom = nrc.get_optical_system().planes[0] assert ote_lom.rms()==0, "RMS WFE should be strictly 0" psf_small = nrc.calc_psf(fov_pixels=50, monochromatic=2e-6, add_distortion=False) centroid = webbpsf.measure_centroid(psf_small, relativeto='center') assert np.abs(centroid[0]) < 1e-5, "Centroid should be (0,0)" assert np.abs(centroid[1]) < 1e-5, "Centroid should be (0,0)"
def load_ote_from_deployment_yaml(deployments_file, out_dir, save=True): """Create a WebbPSF adjustable OTE object representing a perturbed OTE mirror state using mirror deployments defined in a YAML file. Parameters ---------- deployments_file : str Path to YAML file containing deployments data. Expects format as produced by ``mirage.psf.deployments.generate_deployment_errors`` out_dir : str Path to directory in which to save OPD FITS files save : bool, optional Denotes whether to save out the OPD (with and without tip/tilt) as FITs files Returns ------- ote : webbpsf.opds.OTE_Linear_Model_WSS object WebbPSF OTE object describing perturbed OTE state with tip and tilt removed segment_tilts : numpy.ndarray List of X and Y tilts for each mirror segment, in microradians ote_opd_with_tilts : numpy.ndarray Array representing perturbed OTE OPD pupil before tip/tilt is removed """ # Make an adjustable OTE object with WebbPSF nc = webbpsf.NIRCam() nc, ote = webbpsf.enable_adjustable_ote(nc) # Open existing file with previous deployments with open(deployments_file) as f: deployment_errors = yaml.unsafe_load(f) # Create OTE object and list of segment tilts ote, segment_tilts = apply_deployment_errors(ote, deployment_errors, out_dir=out_dir, save=save) ote_opd_with_tilts = ote.opd # Save array to plot below # Remove tip and tilt ote = remove_piston_tip_tilt(ote, out_dir=out_dir, save=save) return ote, segment_tilts, ote_opd_with_tilts
def test_coarse_jitter(): """ Test the jitter options for Coarse Point mode Simple test to verify functionality, and sanity check that more blur means a less sharp PSF. """ nrc = webbpsf.NIRCam() nrc.include_si_wfe = False nrc.options['jitter']='PCS=Coarse' psf_coarse = nrc.calc_psf(nlambda=1, add_distortion=False, fov_pixels=30) nrc.options['jitter']='PCS=Coarse_Like_ITM' psf_coarse_like_itm = nrc.calc_psf(nlambda=1, add_distortion=False, fov_pixels=30) # These test values are handwaved without any particular rigor assert psf_coarse[0].header['JITRSTRL'] < 0.5, "Coarse point blurs the image less than expected" assert psf_coarse_like_itm[0].header['JITRSTRL'] < 0.05, "Coarse point (Like ITM) blurs the image less than expected" assert psf_coarse[0].header['JITRTYPE'].startswith('PCS Coarse'), "Header keyword not as expected" assert 'JITRCPV2' in psf_coarse[0].header, "Header doesn't contain the expected coarse point offset V2 keyword" assert 'JITRCPV3' in psf_coarse[0].header, "Header doesn't contain the expected coarse point offset V3 keyword"
def test_enable_adjustable_ote(): """ Some basic tests of the OTE LOM""" nc = webbpsf.NIRCam() nc, ote = webbpsf.enable_adjustable_ote(nc) # did this produce an OTE object? assert isinstance(ote, webbpsf.opds.OTE_Linear_Model_WSS), "Didn't get an OTE object back" # can we compute the rms? rms = ote.rms() # and can we move a mirror? ote.move_seg_local('B1', piston=10, clocking=200) assert ote.segment_state[6, 2] == 10, "Couldn't piston" assert ote.segment_state[6, 3] == 200, "Couldn't clock" # did that misalignment make things much worse? assert ote.rms() > rms*10, "Huge piston offset didn't make the WFE much worse"
def num_matrix_jwst(): """ Generate a numerical PASTIS matrix for a JWST coronagraph. All inputs are read from the (local) configfile and saved to the specified output directory. """ import webbpsf from e2e_simulators import webbpsf_imaging as webbim # Keep track of time start_time = time.time() # runtime is currently around 21 minutes print('Building numerical matrix for JWST\n') # Parameters resDir = os.path.join(CONFIG_INI.get('local', 'local_data_path'), 'active', 'matrix_numerical') which_tel = CONFIG_INI.get('telescope', 'name') nb_seg = CONFIG_INI.getint(which_tel, 'nb_subapertures') im_size_e2e = CONFIG_INI.getint('numerical', 'im_size_px_webbpsf') inner_wa = CONFIG_INI.getint(which_tel, 'IWA') outer_wa = CONFIG_INI.getint(which_tel, 'OWA') sampling = CONFIG_INI.getfloat('numerical', 'sampling') fpm = CONFIG_INI.get(which_tel, 'focal_plane_mask') # focal plane mask lyot_stop = CONFIG_INI.get(which_tel, 'pupil_plane_stop') # Lyot stop filter = CONFIG_INI.get(which_tel, 'filter_name') nm_aber = CONFIG_INI.getfloat('calibration', 'single_aberration') * u.nm wss_segs = webbpsf.constants.SEGNAMES_WSS_ORDER zern_max = CONFIG_INI.getint('zernikes', 'max_zern') zern_number = CONFIG_INI.getint('calibration', 'zernike') zern_mode = util.ZernikeMode( zern_number) # Create Zernike mode object for easier handling wss_zern_nb = util.noll_to_wss( zern_number) # Convert from Noll to WSS framework # If subfolder "matrix_numerical" doesn't exist yet, create it. if not os.path.isdir(resDir): os.mkdir(resDir) # If subfolder "OTE_images" doesn't exist yet, create it. if not os.path.isdir(os.path.join(resDir, 'OTE_images')): os.mkdir(os.path.join(resDir, 'OTE_images')) # If subfolder "psfs" doesn't exist yet, create it. if not os.path.isdir(os.path.join(resDir, 'psfs')): os.mkdir(os.path.join(resDir, 'psfs')) # If subfolder "darkholes" doesn't exist yet, create it. if not os.path.isdir(os.path.join(resDir, 'darkholes')): os.mkdir(os.path.join(resDir, 'darkholes')) # Create the dark hole mask. pup_im = np.zeros([im_size_e2e, im_size_e2e ]) # this is just used for DH mask generation dh_area = util.create_dark_hole(pup_im, inner_wa, outer_wa, sampling) # Create a direct WebbPSF image for normalization factor fake_aber = np.zeros([nb_seg, zern_max]) psf_perfect = webbim.nircam_nocoro(filter, fake_aber) normp = np.max(psf_perfect) psf_perfect = psf_perfect / normp # Set up NIRCam coro object from WebbPSF nc_coro = webbpsf.NIRCam() nc_coro.filter = filter nc_coro.image_mask = fpm nc_coro.pupil_mask = lyot_stop # Null the OTE OPDs for the PSFs, maybe we will add internal WFE later. nc_coro, ote_coro = webbpsf.enable_adjustable_ote( nc_coro) # create OTE for coronagraph nc_coro.include_si_wfe = False # set SI internal WFE to zero #-# Generating the PASTIS matrix and a list for all contrasts matrix_direct = np.zeros([nb_seg, nb_seg]) # Generate empty matrix all_psfs = [] all_dhs = [] all_contrasts = [] print('nm_aber: {}'.format(nm_aber)) for i in range(nb_seg): for j in range(nb_seg): print('\nSTEP: {}-{} / {}-{}'.format(i + 1, j + 1, nb_seg, nb_seg)) # Get names of segments, they're being addressed by their names in the ote functions. seg_i = wss_segs[i].split('-')[0] seg_j = wss_segs[j].split('-')[0] # Put the aberration on the correct segments Aber_WSS = np.zeros([ nb_seg, zern_max ]) # The Zernikes here will be filled in the WSS order!!! # Because it goes into _apply_hexikes_to_seg(). Aber_WSS[i, wss_zern_nb - 1] = nm_aber.to( u.m ).value # Aberration on the segment we're currently working on; # convert to meters; -1 on the Zernike because Python starts # numbering at 0. Aber_WSS[j, wss_zern_nb - 1] = nm_aber.to( u.m).value # same for other segment # Putting aberrations on segments i and j ote_coro.reset( ) # Making sure there are no previous movements on the segments. ote_coro.zero() # set OTE for coronagraph to zero # Apply both aberrations to OTE. If i=j, apply only once! ote_coro._apply_hexikes_to_seg(seg_i, Aber_WSS[ i, :]) # set segment i (segment numbering starts at 1) if i != j: ote_coro._apply_hexikes_to_seg(seg_j, Aber_WSS[j, :]) # set segment j # If you want to display it: # ote_coro.display_opd() # plt.show() # Save OPD images for testing opd_name = 'opd_' + zern_mode.name + '_' + zern_mode.convention + str( zern_mode.index) + '_segs_' + str(i + 1) + '-' + str(j + 1) plt.clf() ote_coro.display_opd() plt.savefig(os.path.join(resDir, 'OTE_images', opd_name + '.pdf')) print('Calculating WebbPSF image') image = nc_coro.calc_psf(fov_pixels=int(im_size_e2e), oversample=1, nlambda=1) psf = image[0].data / normp # Save WebbPSF image to disk filename_psf = 'psf_' + zern_mode.name + '_' + zern_mode.convention + str( zern_mode.index) + '_segs_' + str(i + 1) + '-' + str(j + 1) util.write_fits(psf, os.path.join(resDir, 'psfs', filename_psf + '.fits'), header=None, metadata=None) all_psfs.append(psf) print('Calculating mean contrast in dark hole') dh_intensity = psf * dh_area contrast = np.mean(dh_intensity[np.where(dh_intensity != 0)]) print('contrast:', contrast) # Save DH image to disk and put current contrast in list filename_dh = 'dh_' + zern_mode.name + '_' + zern_mode.convention + str( zern_mode.index) + '_segs_' + str(i + 1) + '-' + str(j + 1) util.write_fits(dh_intensity, os.path.join(resDir, 'darkholes', filename_dh + '.fits'), header=None, metadata=None) all_dhs.append(dh_intensity) all_contrasts.append(contrast) # Fill according entry in the matrix matrix_direct[i, j] = contrast # Transform saved lists to arrays all_psfs = np.array(all_psfs) all_dhs = np.array(all_dhs) all_contrasts = np.array(all_contrasts) # Filling the off-axis elements matrix_two_N = np.copy( matrix_direct ) # This is just an intermediary copy so that I don't mix things up. matrix_pastis = np.copy( matrix_direct) # This will be the final PASTIS matrix. for i in range(nb_seg): for j in range(nb_seg): if i != j: matrix_off_val = (matrix_two_N[i, j] - matrix_two_N[i, i] - matrix_two_N[j, j]) / 2. matrix_pastis[i, j] = matrix_off_val print('Off-axis for i{}-j{}: {}'.format( i + 1, j + 1, matrix_off_val)) # Normalize matrix for the input aberration matrix_pastis /= np.square(nm_aber.value) # Save matrix to file filename_matrix = 'PASTISmatrix_num_' + zern_mode.name + '_' + zern_mode.convention + str( zern_mode.index) util.write_fits(matrix_pastis, os.path.join(resDir, filename_matrix + '.fits'), header=None, metadata=None) print('Matrix saved to:', os.path.join(resDir, filename_matrix + '.fits')) # Save the PSF and DH image *cubes* as well (as opposed to each one individually) util.write_fits(all_psfs, os.path.join(resDir, 'psfs', 'psf_cube' + '.fits'), header=None, metadata=None) util.write_fits(all_dhs, os.path.join(resDir, 'darkholes', 'dh_cube' + '.fits'), header=None, metadata=None) np.savetxt(os.path.join(resDir, 'contrasts.txt'), all_contrasts, fmt='%e') # Tell us how long it took to finish. end_time = time.time() print('Runtime for matrix_building.py:', end_time - start_time, 'sec =', (end_time - start_time) / 60, 'min') print('Data saved to {}'.format(resDir))
# And now, let's make a plot of the original image. plt.figure(figsize=(9, 9)) imshow_norm(data, interval=PercentileInterval(99.), stretch=SqrtStretch()) plt.colorbar() plt.savefig(data_file + '.png', dpi=300) plt.clf() # And let's make a plot of the image showing the positions of the objects. plt.figure(figsize=(9, 9)) imshow_norm(data, interval=PercentileInterval(99.), stretch=SqrtStretch()) plt.scatter(init_tbl['x_0'], init_tbl['y_0'], s=10, color='black') plt.colorbar() plt.savefig(data_file + '_with_daostarfinder_objects.png', dpi=300) # Let's go and load up the psf grid, which will be 3x3 for now. nrc = webbpsf.NIRCam() nrc.filter = hdu[0].header['FILTER'] #"F150W" if (hdu[0].header['DETECTOR'] == 'NRCALONG'): nrc.detector = 'NRCA5' elif (hdu[0].header['DETECTOR'] == 'NRCBLONG'): nrc.detector = 'NRCB5' else: nrc.detector = hdu[0].header['DETECTOR'] #'NRCA3' #nrc_grid = nrc.psf_grid(num_psfs=9, all_detectors=False) nrc_grid = nrc.psf_grid(num_psfs=25, all_detectors=False, fov_pixels=33, oversample=2) eval_xshape = int(np.ceil(nrc_grid.data.shape[2] / nrc_grid.oversampling)) eval_yshape = int(np.ceil(nrc_grid.data.shape[1] / nrc_grid.oversampling))
# Note: meant to just copy/paste into ipython command line # Import WebbPSF and setup multiprocessing import webbpsf webbpsf.webbpsf_core.poppy.conf.use_fftw = False webbpsf.webbpsf_core.poppy.conf.use_multiprocessing = True ncores = 8 webbpsf.webbpsf_core.poppy.conf.n_processes = ncores inst = webbpsf.NIRCam() inst.filter = "F430M" inst.detector_position = (1024,1024) # Baseline test: No SI WFE, no distortion inst.include_si_wfe = False %timeit psf = inst.calc_psf(add_distortion=False, monochromatic=4.3e-6) # Result: 911 ms ± 5.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) %timeit psf = inst.calc_psf(add_distortion=False, nlambda=ncores) # Result: 5.62 s ± 177 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) # Turn on SI WFE at center of detector (will use interpolation) inst.include_si_wfe = True %timeit psf = inst.calc_psf(add_distortion=False, monochromatic=4.3e-6) # Result: 1.41 s ± 12.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) %timeit psf = inst.calc_psf(add_distortion=False, nlambda=ncores) # Result: 6.1 s ± 96.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) # Use pixel (0,0) to force extrapolation algorithm inst.detector_position = (0,0) %timeit psf = inst.calc_psf(add_distortion=False, monochromatic=4.3e-6) # Result: 1.8 s ± 12.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
def generate_wavefront_errors(nb_of_maps, errors, nb_zernikes, path): import poppy, webbpsf import random import matplotlib.pyplot as plt # intial wavefront map nc = webbpsf.NIRCam() nc, ote = webbpsf.enable_adjustable_ote(nc) osys = nc._get_aberrations() # perturbed wavefront map nc_perturb = webbpsf.NIRCam() nc_perturb, ote_perturb = webbpsf.enable_adjustable_ote(nc_perturb) osys_perturb = nc_perturb._get_aberrations() # final wavefront map nc_final = webbpsf.NIRCam() nc_final, ote_final = webbpsf.enable_adjustable_ote(nc_final) osys_final = nc_final._get_aberrations() tab_opd_final = [] for n, error in zip(range(nb_of_maps), errors): print(n, error) # change aberrations in wavefront map: example with random zernikes # this map will be our perturbation map and we will add it to the initial map with a certain weight # creating the perturbation map #weight = 0.2 weight = error / 100 for i in range(nb_zernikes): #tmp = random.randint(-10,10) tmp = random.randint(-1, 1) osys_perturb.zernike_coeffs[ i] = weight * tmp * osys.zernike_coeffs[i] osys_final.zernike_coeffs[i] = osys.zernike_coeffs[ i] + weight * tmp * osys.zernike_coeffs[i] # implementing and displaying the wavefront maps #display_ote_and_psf(nc, ote, title="Initial OPD and PSF") ote_perturb.reset() ote_perturb.move_global_zernikes(osys_perturb.zernike_coeffs[0:10]) #display_ote_and_psf(nc_perturb, ote_perturb, title="Perturbed OPD and PSF") ote_final.reset() ote_final.move_global_zernikes(osys_final.zernike_coeffs[0:10]) #display_ote_and_psf(nc_final, ote_final, title="Final OPD and PSF") rms = ote.rms() rms_perturb = ote_perturb.rms() rms_final = ote_final.rms() print(rms, rms_perturb, rms_final) print('') #print(osys.zernike_coeffs) #print('') #print(osys_perturb.zernike_coeffs) #print('') #print(osys_final.zernike_coeffs) #print('') opd = poppy.zernike.opd_from_zernikes( osys.zernike_coeffs[0:10], npix=1024, basis=poppy.zernike.zernike_basis_faster) opd_perturb = poppy.zernike.opd_from_zernikes( osys_perturb.zernike_coeffs[0:10], npix=1024, basis=poppy.zernike.zernike_basis_faster) opd_final = poppy.zernike.opd_from_zernikes( osys_final.zernike_coeffs[0:10], npix=1024, basis=poppy.zernike.zernike_basis_faster) #tab_opd_final.append(opd_final) write_fits(path + '_opd' + str(n) + '.fits', opd) write_fits(path + '_opd_perturb' + str(n) + '.fits', opd_perturb) write_fits(path + '_opd_final' + str(n) + '.fits', opd_final) #plt.figure(figsize=(12,4)) #ax1 = plt.subplot(131) #ax1.imshow(opd) #ax1.set_title('initial wavefront map') #ax2 = plt.subplot(132) #ax2.imshow(opd_perturb) #ax2.set_title('perturbed wavefront map') #ax3 = plt.subplot(133) #ax3.imshow(opd_final) #ax3.set_title('sum of maps') #plt.show() wavefront_error = mse(np.nan_to_num(opd), np.nan_to_num(opd_final)) print('mse', error, wavefront_error) print("MSE: %.2f" % (wavefront_error * 100)) return tab_opd_final
def resetPSF(self): import webbpsf if self.filter not in self.FILTERS: raise ValueError("Filter %s is not a valid %s filter" % (self.filter, self.classname)) have_psf = False if os.path.exists(os.path.join(self.out_path, "psf_cache")): if os.path.exists( os.path.join( self.out_path, "psf_cache", "psf_{}_{}_{}.fits".format("NIRCam", self.filter, self.oversample))): with pyfits.open( os.path.join( self.out_path, "psf_cache", "psf_{}_{}_{}.fits".format( "NIRCam", self.filter, self.oversample))) as psf: if psf[0].header['VERSION'] >= webbpsf.__version__ and ( self.psf_commands is None or self.psf_commands == ''): self.psf = AstroImage(data=psf[0].data, detname="NIRCam {} PSF".format( self.filter), logger=self.logger) have_psf = True if not have_psf: base_state = self.getState() self.updateState( base_state + "<br /><span class='indented'>Generating PSF</span>") ins = webbpsf.NIRCam() if self.psf_commands is not None and self.psf_commands != '': for attribute, value in self.psf_commands.iteritems(): setattr(ins, attribute, value) ins.filter = self.filter max_safe_size = int( np.floor(30. * self.PHOTPLAM[self.filter] / (2. * self.SCALE[0]))) max_ins_size = max(self.DETECTOR_SIZE) * self.oversample max_conv_size = int(np.floor(2048 / self.oversample)) self._log( "info", "PSF choosing between {}, {}, and {}".format( max_safe_size, max_ins_size, max_conv_size)) psf = ins.calcPSF(oversample=self.oversample, fov_pixels=min(max_safe_size, max_ins_size, max_conv_size)) psf[0].header['VERSION'] = webbpsf.__version__ if os.path.exists(os.path.join(self.out_path, "psf_cache")): dest = os.path.join( self.out_path, "psf_cache", "psf_{}_{}_{}.fits".format("NIRCam", self.filter, self.oversample)) pyfits.writeto(dest, psf[0].data, header=psf[0].header, overwrite=True) self.psf = AstroImage(data=psf[0].data, detname="NIRCam %s PSF" % (self.filter), logger=self.logger) self.updateState(base_state)
def generate_wavefront_errors_correction(nb_of_maps, errors, nb_zernikes): import poppy, webbpsf import matplotlib.pyplot as plt # intial wavefront map nc = webbpsf.NIRCam() nc, ote = webbpsf.enable_adjustable_ote(nc) osys = nc._get_aberrations() # final wavefront map nc_final = webbpsf.NIRCam() nc_final, ote_final = webbpsf.enable_adjustable_ote(nc_final) osys_final = nc_final._get_aberrations() tab_wavefront_error = np.zeros(nb_of_maps) tab_error = np.zeros(nb_of_maps) for n, error in zip(range(nb_of_maps), errors): print(n, error) #print(zip(range(nb_of_maps))) #print(errors) # change aberrations in wavefront map: example with random zernikes # this map will be our perturbation map and we will add it to the initial map with a certain weight # creating the perturbation map #weight = 0.2 #weight = error/100 osys_corrected = osys.zernike_coeffs.copy() for i in range(nb_zernikes): if i < error + 1: osys_corrected[i] = 0 print(osys.zernike_coeffs) print(osys_corrected) opd = poppy.zernike.opd_from_zernikes( osys.zernike_coeffs, npix=1024, basis=poppy.zernike.zernike_basis_faster) opd_corrected = poppy.zernike.opd_from_zernikes( osys_corrected, npix=1024, basis=poppy.zernike.zernike_basis_faster) wavefront_error = mse(np.nan_to_num(opd), np.nan_to_num(opd_corrected)) print('mse', error, wavefront_error) #tab_opd_final.append(opd_final) #write_fits('_opd'+str(n)+'.fits',opd) #write_fits('_opd_corrected'+str(n)+'.fits',opd_corrected) plt.figure(figsize=(12, 4)) ax1 = plt.subplot(131) #ax1.imshow(opd,vmin=np.min(opd),vmax=np.max(opd)) ax1.imshow(opd) ax1.set_title('initial wavefront map') ax2 = plt.subplot(132) #ax2.imshow(opd_corrected,vmin=np.min(opd),vmax=np.max(opd)) ax2.imshow(opd_corrected) ax2.set_title('corrected wavefront map') ax3 = plt.subplot(133) ax3.imshow(opd - opd_corrected) ax3.set_title('sum of maps') plt.show() tab_wavefront_error[n] = wavefront_error tab_error[n] = error return tab_wavefront_error, tab_error
def test_visualize_wfe_budget(): """Basic test that the visual optical budget functionality at least runs without raising an error or exception No actual checking of the output plots is performed. """ nrc = webbpsf.NIRCam() nrc.visualize_wfe_budget()
def set_sim_params(args): #logging.basicConfig(level=logging.INFO, format='%(name)-12s: %(levelname)-8s %(message)s',) print "WebbPSF",pkg_resources.get_distribution("webbpsf").version," Poppy",pkg_resources.get_distribution("poppy").version #webbpsf.setup_logging(level='ERROR') nruns= args.nruns # filt = args.f.upper() #e.g. F430M mask_coron=args.mask.upper() #e.g. MASK430R pupil_stop=args.stop.upper() #e.g. CIRCLYOT instr=args.I.upper() if instr=='NIRCAM': lambda0=float(filt[1:4])/100 elif instr=='MIRI': lambda0=float(filt[1:5])/100 jitter= args.jitter grid_step=args.gstep gridpoints=args.gnpts side=int(np.sqrt(gridpoints)) max_step=np.floor(side/2) rms= args.rms fovarcsec = args.fov #default 7.04 #fovpixels = 64 sigmaTA = 4.7 sigmaFSM= 2.0 if instr=='NIRCAM': img=webbpsf.NIRCam() elif instr=='MIRI': img=webbpsf.MIRI() if filt not in img.filter_list: if instr == 'MIRI' : print "\nFilter "+mask_coron+" not available. Select from: ",[f for f in img.filter_list if "C" in f[-1]],'\n' if instr == 'NIRCAM': print "\nFilter "+mask_coron+" not available. Select from: ",img.filter_list,'\n' sys.exit() if mask_coron not in img.image_mask_list: print "\nCoronagraphic mask "+mask_coron+" not available. Select from: ",img.image_mask_list,'\n' sys.exit() if pupil_stop not in img.pupil_mask_list[:2]: print "\nPupil mask "+pupil_stop+" not available. Select from: ",img.pupil_mask_list[:2],'\n' sys.exit() opd_rms = [int(img.opd_list[i][-8:-5]) for i in xrange(len(img.opd_list))] if rms not in opd_rms: print "\nOPD rms ("+str(rms)+") not available. Select from: ",opd_rms,'\n' sys.exit() if instr=='NIRCAM' and args.noopd==False: opd='/Users/lajoie/WebbPSF/webbpsf-data/NIRCam/OPD/OPD_RevV_nircam_'+str(rms)+'.fits' if instr=='MIRI' and args.noopd==False: opd='/Users/lajoie/WebbPSF/webbpsf-data/MIRI/OPD/OPD_RevV_miri_'+str(rms)+'.fits' img.filter=filt img._rotation=0. img.options["output_mode"]='detector sampled' if args.noopd==False: img.pupilopd = (opd, 0) #select FITS extension for OPD print opd if instr=='NIRCAM': outdir='./Results'+'/'+instr+'/'+filt+'_'+mask_coron+'/DitherStep'+str(int(grid_step))+'/Jitter'+str(int(jitter))+'/rms'+str(rms)+'nm/' else: outdir='./Results'+'/'+instr+'/'+filt+'/DitherStep'+str(int(grid_step))+'/Jitter'+str(int(jitter))+'/rms'+str(rms)+'nm/' if gridpoints != 9: raw_input(" *** generating grids of %i points: ENTER to continue " %gridpoints) outdir = outdir[:-1]+"_Grid_%i_pts/" %gridpoints if not os.path.exists(outdir): os.makedirs(outdir) x0,y0 = get_source_offset(lambda0, filt, mask_coron) if np.abs(x0) > fovarcsec/2: print "\n optimal bar occulter offset out of FOV. Please fix FOV in code (fovarcsec).\n x_offset=%f Total fov=%f\n" %(x0,fovarcsec) sys.exit() generate_PSF(img, outdir, nruns, x0, y0, grid_step, side, max_step, sigmaTA, sigmaFSM, mask_coron, pupil_stop, lambda0, jitter, rms, fovarcsec) return
def test_segment_tilt_signs(fov_pix=50, plot=False): """Test that segments move in the direction expected when tilted. The local coordinate systems are non-obvious, to say the least. This verifies sign conventions and coordinates are consistent in the linear optical model and optical propagation code. """ if plot: fig, axs = plt.subplots(3, 5, figsize=(14, 9)) #, sharex = True, sharey = True) nrc = webbpsf.NIRCam() ote = webbpsf.opds.OTE_Linear_Model_WSS() nrc.include_si_wfe = False # not relevant for this test tilt = 1.0 # We im for relatively minimalist PSF calcs, to reduce test runtime psf_kwargs = { 'monochromatic': 2e-6, 'fov_pixels': fov_pix, 'oversample': 1, 'add_distortion': False } # Which way are things expected to move? # # A1: +X rotation -> -Y pixels (DMS), +Y rotation -> -X pixels # B1: +X rotation -> +Y pixels, +Y rotation -> +X pixels # C1: +X rotation -> +X/+Y pixels, +Y rotation -> -Y/+X pixels # (for C1, A/B means A is the sqrt(3)/2 component, B is the 1/2 component) # # The above derived from Code V models by R. Telfer, subsequently cross checked by Perrin for i, iseg in enumerate(['A1', 'B1', 'C1']): ote.zero() pupil = webbpsf.webbpsf_core.one_segment_pupil(iseg) ote.amplitude = pupil[0].data nrc.pupil = ote # CENTERED PSF: psf = nrc.calc_psf(**psf_kwargs) cen_ref = webbpsf.measure_centroid(psf, boxsize=10, threshold=1) ote.move_seg_local(iseg, xtilt=tilt) # XTILT PSF: psfx = nrc.calc_psf(**psf_kwargs) cen_xtilt = webbpsf.measure_centroid(psfx, boxsize=10, threshold=1) if iseg.startswith("A"): assert cen_xtilt[0] < cen_ref[ 0], "Expected A1: +X rotation -> -Y pixels (DMS coords)" assert np.isclose( cen_xtilt[1], cen_ref[1], atol=1), "Expected A1: +X rotation -> no change in X" elif iseg.startswith("A"): assert cen_xtilt[0] > cen_ref[ 0], "Expected B1: +X rotation -> +Y pixels(DMS coords)" assert np.isclose( cen_xtilt[1], cen_ref[1], atol=1), "Expected B1: +X rotation -> no change in Y" elif iseg.startswith("C"): assert cen_xtilt[0] > cen_ref[ 0], "Expected C1: +X rotation -> +X/+Y pixels" assert cen_xtilt[1] > cen_ref[ 1], "Expected C1: +X rotation -> +X/+Y pixels" if plot: axs[i, 0].imshow(psf[0].data, norm=matplotlib.colors.LogNorm(vmax=1e-2, vmin=1e-5), origin="lower") axs[i, 0].set_title(iseg + ": centered") axs[i, 0].axhline(y=fov_pix / 2) axs[i, 0].axvline(x=fov_pix / 2) # PLOT RESULTING OPD: im = axs[i, 1].imshow(ote.opd, vmin=-4e-6, vmax=4e-6, origin="lower") axs[i, 1].set_title("OPD (yellow +)") axs[i, 2].imshow(psfx[0].data, norm=matplotlib.colors.LogNorm(vmax=1e-2, vmin=1e-5), origin="lower") axs[i, 2].set_title(iseg + ": xtilt {} um".format(tilt)) axs[i, 2].axhline(y=fov_pix / 2) axs[i, 2].axvline(x=fov_pix / 2) ote.zero() ote.move_seg_local(iseg, ytilt=tilt) # YTILT PSF: psfy = nrc.calc_psf(**psf_kwargs) cen_ytilt = webbpsf.measure_centroid(psfy, boxsize=10, threshold=1) if iseg.startswith("A"): assert cen_ytilt[1] < cen_ref[ 1], "Expected A1: +Y rotation -> -X pixels (DMS coords)" assert np.isclose( cen_ytilt[0], cen_ref[0], atol=1), "Expected A1: +Y rotation -> no change in Y" elif iseg.startswith("A"): assert cenyxtilt[0] > cen_ref[ 0], "Expected B1: +Y rotation -> +X pixels(DMS coords)" assert np.isclose( cen_xtilt[0], cen_ref[0], atol=1), "Expected B1: +Y rotation -> no change in Y" elif iseg.startswith("C"): assert cen_ytilt[0] < cen_ref[ 0], "Expected C1: +Y rotation -> -Y/+X pixels" assert cen_ytilt[1] > cen_ref[ 1], "Expected C1: +Y rotation -> -Y/+X pixels" # PLOT RESULTING OPD: if plot: im = axs[i, 3].imshow(ote.opd, vmin=-4e-6, vmax=4e-6, origin="lower") axs[i, 3].set_title("OPD (yellow +)") axs[i, 4].imshow(psfy[0].data, norm=matplotlib.colors.LogNorm(vmax=1e-2, vmin=1e-5), origin="lower") axs[i, 4].set_title(iseg + ": ytilt {} um".format(tilt)) axs[i, 4].axhline(y=fov_pix / 2) axs[i, 4].axvline(x=fov_pix / 2)
zern_number) # Convert from Noll to WSS framework # If subfolder "calibration" doesn't exist yet, create it. if not os.path.isdir(outDir): os.mkdir(outDir) # If subfolder "images" in "calibration" doesn't exist yet, create it. if not os.path.isdir(os.path.join(outDir, 'images')): os.mkdir(os.path.join(outDir, 'images')) # Create Zernike mode object for easier handling zern_mode = util.ZernikeMode(zern_number) # Create NIRCam objects, one for perfect PSF and one with coronagraph print('Setting up the E2E simulation.') nc = webbpsf.NIRCam() # Set filter nc.filter = filter # Same for coronagraphic case nc_coro = webbpsf.NIRCam() nc_coro.filter = filter # Add coronagraphic elements to nc_coro nc_coro.image_mask = fpm nc_coro.pupil_mask = lyot_stop # Null the OTE OPDs for the PSFs, maybe we will add internal WFE later. nc, ote = webbpsf.enable_adjustable_ote(nc) # create OTE for default PSF nc_coro, ote_coro = webbpsf.enable_adjustable_ote( nc_coro) # create OTE for coronagraph
def main(): # Here's the path to the data data_path = '/data1/car_24_apt_01073/mirage/reduced/' nrc_sca = 'nrca1' data_files = [ 'jw01073001001_01101_00001_' + nrc_sca + '_cal', 'jw01073001001_01101_00002_' + nrc_sca + '_cal', 'jw01073001002_01101_00003_' + nrc_sca + '_cal', 'jw01073001002_01101_00004_' + nrc_sca + '_cal', 'jw01073001003_01101_00005_' + nrc_sca + '_cal', 'jw01073001003_01101_00006_' + nrc_sca + '_cal', 'jw01073001004_01101_00007_' + nrc_sca + '_cal', 'jw01073001004_01101_00008_' + nrc_sca + '_cal' ] # Let's load in the first file hdu = fits.open(data_path + data_files[0] + '.fits') # Let's start by loading up the psf grid, which will be 3x3 for now. nrc = webbpsf.NIRCam() nrc.filter = hdu[0].header['FILTER'] #"F150W" if (hdu[0].header['DETECTOR'] == 'NRCALONG'): nrc.detector = 'NRCA5' elif (hdu[0].header['DETECTOR'] == 'NRCBLONG'): nrc.detector = 'NRCB5' else: nrc.detector = hdu[0].header['DETECTOR'] #'NRCA3' #nrc_grid = nrc.psf_grid(num_psfs=9, all_detectors=False) nrc_grid = nrc.psf_grid(num_psfs=25, all_detectors=False, fov_pixels=33, oversample=2) # And let's close the first data file. hdu.close() # Now, let's spawn processes that will run on the various images. number_of_processes_at_the_same_time = 8 process_list = [] for i in range(number_of_processes_at_the_same_time): data_file_name = data_files[i] hdu = fits.open(data_path + data_files[i] + '.fits') image = ImageModel(data_path + data_files[i] + '.fits') data_array = hdu[1].data pheader = hdu[0].header fheader = hdu[1].header hdu.close() #print(data_file_name) p = multiprocessing.Process(target=psf_fit, args=(data_array, data_file_name, nrc_grid, fheader, image)) p.start() process_list.append(p) for process in process_list: process.join()
def main(): # save the figures figdir = 'Figures' # just listing the wide filters nircam_bandpasses = 'F070W,F090W,F115W,F150W,F200W,F277W,F356W,F444W' miri_bandpasses = 'F560W,F770W,F1000W,F1130W,F1280W,F1500W,F1800W,F2100W,F2550W' nircam_bandpasses = nircam_bandpasses.split(',') miri_bandpasses = miri_bandpasses.split(',') # configure the instruments nircam = W.NIRCam() miri = W.MIRI() # load the bandpasses bandpasses = {} for bp in nircam_bandpasses: nircam.filter = bp bpmodel = nircam._getSynphotBandpass(nircam.filter) bandpasses[bp] = bpmodel for bp in miri_bandpasses: miri.filter = bp bpmodel = miri._getSynphotBandpass(miri.filter) bandpasses[bp] = bpmodel # we just need a couple of bandpasses for testing use_bandpasses = nircam_bandpasses + miri_bandpasses[0:3] # init the kilonova model and create some arrays to store output kn = Kilonova() # do this for a few distances for j, dmpc in enumerate(DISTANCE): time = [] rfphase = [] flux = {} if j == 0: fig = plt.figure(figsize=(8, 15)) ax = fig.add_subplot(1, 1, 1) for i, phase in enumerate(kn._times): # get the kilonova model and spectrum for this phase and distance lam, flam = kn.get_model(phase) lamz, fnorm = kn.get_norm_model(phase, dmpc) name = 'kilonova_{:+.2f}'.format(phase) spec = S.ArraySpectrum(wave=lamz, flux=fnorm, waveunits='angstrom', fluxunits='flam', name=name) # get synthetic mags in each passband for bp in use_bandpasses: passband = bandpasses[bp] obs = S.Observation(spec, passband, force='taper') try: mag = obs.effstim('abmag') except ValueError as e: mag = np.nan thispb = flux.get(bp) if thispb is None: thispb = [ mag, ] else: thispb.append(mag) flux[bp] = thispb # keep a track of rest-frame phase + observer frame days (should make much difference at these distances) dist = c.Distance(dmpc * u.megaparsec) z = dist.z rfphase.append(phase) time.append(phase * (1. + z)) # write output photometry tables if i % 5 == 0: # convert flam -> fnu -> mJy (YUCK) lam_micron = lamz * ANGSTROM_TO_MICRON f_nu = (((lamz * ANGSTROM_TO_CM)**2.) / SPEED_OF_LIGHT) * fnorm / ANGSTROM_TO_MICRON f_mjy = f_nu * FNU_TO_MJY table_name = 'Tables/kilonova_orig_{}Mpc_p{:+.2f}.txt'.format( dmpc, phase) this_spec = at.Table([lam_micron, f_mjy], names=['wave_micron', 'flux_mjy']) this_spec.sort('wave_micron') this_spec.write(table_name, format='ascii.fixed_width', delimiter=' ', overwrite='True') # plot output spectral sequence if j == 0: fplot = flam / flam.mean() ax.plot(lam * ANGSTROM_TO_MICRON, fplot + 180 - i, 'k-') # finalize spectral sequence plot if j == 0: ax.tick_params(axis='both', which='major', labelsize='large') ax.set_xlabel(r'Rest Wavelength ($\mu$m)', fontsize='xx-large') ax.set_ylabel(r'Relative F$_{\lambda}$ + constant', fontsize='xx-large') ax.set_xlim(0.5, 9.5) fig.tight_layout(rect=[0, 0, 1, 0.96]) plt.savefig('{}/kilonova_spec.pdf'.format(figdir)) plt.close(fig) # dump output mag tables arrays = [ rfphase, time, ] + [flux[bp] for bp in use_bandpasses] names = ['rfphase', 'ofphase'] + [bp for bp in use_bandpasses] out = at.Table(arrays, names=names) out.write('Tables/kilonova_phottable_{}Mpc.txt'.format(dmpc), delimiter=' ', format='ascii.fixed_width', overwrite=True) # plot up the lightcurves npbs = len(use_bandpasses) color = iter(plt.cm.tab20(np.linspace(0, 1, npbs))) with PdfPages('{}/kilonova_phot_{}Mpc.pdf'.format(figdir, dmpc)) as pdf: fig = plt.figure(figsize=(10, 10)) for i, bp in enumerate(use_bandpasses): # save four passbands per page if i % 4 == 0 and i > 0: fig.suptitle( 'Kilonova Synthetic Photometry {} Mpc'.format(dmpc), fontsize='xx-large') fig.tight_layout(rect=[0, 0, 1, 0.93]) pdf.savefig(fig) plt.close(fig) fig = plt.figure(figsize=(10, 10)) # plot up a passband ax = fig.add_subplot(2, 2, i % 4 + 1) thiscol = next(color) ax.plot(out['ofphase'], out[bp], marker='o', linestyle='-', lw=0.5, label=bp, color=thiscol) ax.tick_params(axis='both', which='major', labelsize='large') ax.set_ylabel('{} (AB mag)'.format(bp), fontsize='xx-large') ax.set_xlabel('Observer-frame Phase (Days)', fontsize='xx-large') ax.legend(loc='upper right', frameon=False) ymin, ymax = ax.get_ylim() ax.set_ylim((ymax, ymin)) # finalize lightcurve plot if i == npbs - 1: fig.suptitle( 'Kilonova Synthetic Photometry {} Mpc'.format(dmpc), fontsize='xx-large') fig.tight_layout(rect=[0, 0, 1, 0.93]) pdf.savefig(fig) plt.close(fig)