def __init__(self, electric_field, wavelength=1, input_stokes_vector=None, pad_factor=1): shape = electric_field.shaped.shape assert shape[0] == shape[1], "simulation region must be square!" assert shape[ 0] % 2 == 0, "simulation region must be an even number of pixels across!" res = shape[0] new_field = np.pad(electric_field.shaped, int(res * (pad_factor - 1) / 2)).flatten() self.radius = electric_field.grid[-1, 0] self.total_radius = self.radius * pad_factor self.pad_factor = pad_factor self.beam_res = res self.res = pad_factor * res padded_electric_field = hc.Field( new_field, hc.make_pupil_grid(pad_factor * res, 2 * pad_factor * self.radius)) super().__init__(padded_electric_field, wavelength, input_stokes_vector)
def remove_pad(wf, pad): res = wf.electric_field.shaped.shape[0] radius = wf.electric_field.grid[-1, 0] beamres = int(res / pad) padpix = int((pad - 1) * beamres / 2) field = wf.electric_field.shaped[padpix:-padpix, padpix:-padpix] grid = hc.make_pupil_grid(beamres, 2 * radius) return hc.Wavefront(hc.Field(field.flatten(), grid), wavelength=wf.wavelength)
def __init__(self, num_act_across, x, diam): # TODO: implement actuator masking and count for total actuators self.M = len(x) self.diam = diam # TODO: unused self.pupil_grid = hcipy.make_pupil_grid(self.M) self.num_act_across = num_act_across self.actuator_spacing = 1. / num_act_across self.influence_functions = hcipy.make_gaussian_influence_functions( self.pupil_grid, self.num_act_across, self.actuator_spacing) self.dm = hcipy.DeformableMirror(self.influence_functions) self.influence_function_matrix = self.dm.influence_functions.transformation_matrix self.command = np.zeros((num_act_across, num_act_across), dtype=np.float64)
def run_loyt(): from hcipy import make_pupil_grid, make_focal_grid, FraunhoferPropagator, circular_aperture, evaluate_supersampled, imshow_field, imsave_field, Field, LyotCoronagraph, Wavefront import numpy as np import matplotlib.pyplot as plt pupil_grid = make_pupil_grid(256) focal_grid = make_focal_grid(pupil_grid, 8, 32) prop = FraunhoferPropagator(pupil_grid, focal_grid) aperture = circular_aperture(1) aperture = evaluate_supersampled(aperture, pupil_grid, 8) mask = 1 - circular_aperture(3)(focal_grid) stop = circular_aperture(0.7)(pupil_grid) coro = LyotCoronagraph(pupil_grid, mask, stop) wf = Wavefront(aperture) lyot = coro(wf) img = prop(lyot) img_ref = prop(wf) output_path = get_output_path("miscellaneous") fig = plt.figure() imshow_field(np.log10(img.power / img_ref.power.max()), vmin=-6, vmax=0) plt.xlim(-25, 25) plt.ylim(-25, 25) cbar = plt.colorbar() cbar.set_label(r"$^{10}\log(contrast)$") plt.ylabel(r"$\lambda/D$") plt.xlabel(r"$\lambda/D$") fig.savefig(output_path + "lyot_psf") fig = plt.figure() imshow_field(np.log10(img_ref.power / img_ref.power.max()), vmin=-6, vmax=0) plt.xlim(-25, 25) plt.ylim(-25, 25) cbar = plt.colorbar() cbar.set_label(r"$^{10}\log(contrast)$") plt.ylabel(r"$\lambda/D$") plt.xlabel(r"$\lambda/D$") fig.savefig(output_path + "normal_psf")
def run_loyt(): from hcipy import make_pupil_grid, make_focal_grid, FraunhoferPropagator, circular_aperture, evaluate_supersampled, imshow_field, imsave_field, Field, LyotCoronagraph, Wavefront import numpy as np import matplotlib.pyplot as plt pupil_grid = make_pupil_grid(256) focal_grid = make_focal_grid(pupil_grid, 8, 32) prop = FraunhoferPropagator(pupil_grid, focal_grid) aperture = circular_aperture(1) aperture = evaluate_supersampled(aperture, pupil_grid, 8) mask = 1 - circular_aperture(3)(focal_grid) stop = circular_aperture(0.7)(pupil_grid) coro = LyotCoronagraph(pupil_grid, mask, stop) wf = Wavefront(aperture) lyot = coro(wf) img = prop(lyot) img_ref = prop(wf) output_path = get_output_path("miscellaneous") fig = plt.figure() imshow_field(np.log10(img.power / img_ref.power.max()), vmin=-6, vmax=0) plt.xlim(-25,25) plt.ylim(-25,25) cbar = plt.colorbar() cbar.set_label(r"$^{10}\log(contrast)$") plt.ylabel(r"$\lambda/D$") plt.xlabel(r"$\lambda/D$") fig.savefig(output_path+"lyot_psf") fig = plt.figure() imshow_field(np.log10(img_ref.power / img_ref.power.max()), vmin=-6, vmax=0) plt.xlim(-25,25) plt.ylim(-25,25) cbar = plt.colorbar() cbar.set_label(r"$^{10}\log(contrast)$") plt.ylabel(r"$\lambda/D$") plt.xlabel(r"$\lambda/D$") fig.savefig(output_path+"normal_psf")
def _setup_grids(self): """ Set up the grids to compute the segmented mirror surface into. This is relatively slow, but we only need to do this once for each size of input grids. """ npix = self.aperture.shaped.shape[0] if npix == self._last_npix: return else: self._last_npix = npix x, y = self.input_grid.coords self._seg_mask = np.zeros_like(x) self._seg_x = np.zeros_like(x) self._seg_y = np.zeros_like(y) self._seg_indices = dict() pupil_grid = hcipy.make_pupil_grid(dims=npix, diameter=PUP_DIAMETER) aper_num, seg_positions = get_atlast_aperture( normalized=False, segment_transmissions=np.arange(1, self.segnum + 1)) aper_num = hcipy.evaluate_supersampled(aper_num, pupil_grid, 2) self._seg_mask = np.copy(aper_num) for i in self.segmentlist: wseg = np.where(self._seg_mask == i) self._seg_indices[i] = wseg cenx, ceny = self.seg_pos.points[i - 1] self._seg_x[wseg] = x[wseg] - cenx self._seg_y[wseg] = y[wseg] - ceny # Set gaps to zero bad_gaps_x = np.where( np.abs(self._seg_x) > 0.1 * PUP_DIAMETER ) #*PUP_DIAMETER generalizes it for any size pupil field self._seg_x[bad_gaps_x] = 0 bad_gaps_y = np.where(np.abs(self._seg_y) > 0.1 * PUP_DIAMETER) self._seg_y[bad_gaps_y] = 0
def build_DM(grid, diam=telescope_diameter): ''' Generate a DM like how I've done ''' num_acts = 21 #21 x 21 DM (a lot of actuators are outside the aperture) act_spacing = diam / num_acts influence_functions = hci.make_gaussian_influence_functions( grid, num_acts, act_spacing) DM = hci.DeformableMirror(influence_functions) n_modes = DM.num_actuators DM.flatten() grid_size = np.mean(grid.delta * grid.dims) dm_grid = hci.make_pupil_grid(num_acts, grid_size) fft_grid = hci.make_fft_grid(dm_grid) Fouriers = hci.make_fourier_basis(dm_grid, fft_grid) Fouriers = hci.ModeBasis([mode for mode in Fouriers.orthogonalized], dm_grid) DM.actuators = Fouriers[0] return DM, dm_grid, Fouriers
def init_collimator(self, beam_radius, col_res=512): """the collimator can run on a higher res than regular pupil plane so that aliasing is prevented when also using PIAA""" self.col_args = (beam_radius, col_res) print("setting up collimator...") self.collimator_grid = hc.make_pupil_grid(col_res, diameter=beam_radius * 2) self.propagator = hc.FraunhoferPropagator( self.collimator_grid, self.focal_grid, focal_length=self.fnum * 2 * beam_radius) # what's the focal length after PIAA??? def _inner_(wf): _power = wf.total_power reals, imags = wf.real, wf.imag reals = np.reshape(reals, (self.hi_pupil_res, self.hi_pupil_res)) imags = np.reshape(imags, (self.hi_pupil_res, self.hi_pupil_res)) if self.hi_pupil_res != col_res: reals = resize2(reals, (col_res, col_res)).flatten() imags = resize2(imags, (col_res, col_res)).flatten() else: reals = reals.flatten() imags = imags.flatten() new_wf = hc.Wavefront( hc.Field(reals + 1.j * imags, self.collimator_grid), wf.wavelength) # make sure power is conserved new_wf.total_power = _power return new_wf self.collimate = _inner_ print("collimator setup complete")
print("generating: "+str(numb)+" psfs with:\n fried parameter: "+str(fried_parameter)+"\n sample time: "+str(time_between)) D_tel = 8.2 # meter wavelength = 1e-6 # meter oversampling = 8 # loading the files required for generating the vAPP. amplitude_temp = read_fits(amplitude_file) phase_temp = read_fits(phase_file) # number of pixels along one axis in the pupil Npix = amplitude_temp.shape[0] # generating the grids pupil_grid = make_pupil_grid(Npix, D_tel) focal_grid = make_focal_grid(pupil_grid, 4, 25, wavelength=wavelength) #kolmogorov: verdeling voor sterkte special freq spectral_noise_factory = SpectralNoiseFactoryFFT(kolmogorov_psd, pupil_grid, oversampling) turbulence_layers = make_standard_multilayer_atmosphere(fried_parameter=fried_parameter, wavelength=wavelength) # create phase screen atmospheric_model = AtmosphericModel(spectral_noise_factory, turbulence_layers) # Mapping from pupil plane to focal plane prop = FraunhoferPropagator(pupil_grid, focal_grid) # converting the amplitude and phase to fields amplitude = Field(amplitude_temp.ravel(), pupil_grid) phase = Field(phase_temp.ravel(), pupil_grid)
def seg_mirror_test(): """ Testing the integrated energy of images produced by HCIPy vs Poppy segmented DMs. This is now deprecated as I am using directly the hcipy SM, but specifically from an older commit: from hcipy.optics.segmented_mirror import SegmentedMirror """ # Parameters which_tel = CONFIG_PASTIS.get('telescope', 'name') NPIX = CONFIG_PASTIS.getint('numerical', 'tel_size_px') PUP_DIAMETER = CONFIG_PASTIS.getfloat(which_tel, 'diameter') GAPSIZE = CONFIG_PASTIS.getfloat(which_tel, 'gaps') FLATTOFLAT = CONFIG_PASTIS.getfloat(which_tel, 'flat_to_flat') wvln = 638e-9 lamD = 20 samp = 4 norm = False fac = 6.55 # --------------------------------- # #aber_rad = 6.2 aber_array = np.linspace(0, 2 * np.pi, 50, True) log.info('Aber in rad: \n{}'.format(aber_array)) log.info('Aber in m: \n{}'.format(util.aber_to_opd(aber_array, wvln))) # --------------------------------- # ### HCIPy SM # HCIPy grids and propagator pupil_grid = hcipy.make_pupil_grid(dims=NPIX, diameter=PUP_DIAMETER) focal_grid = hcipy.make_focal_grid(pupil_grid, samp, lamD, wavelength=wvln) prop = hcipy.FraunhoferPropagator(pupil_grid, focal_grid) # Generate an aperture aper, seg_pos = get_atlast_aperture(normalized=norm) aper = hcipy.evaluate_supersampled(aper, pupil_grid, 1) # Instantiate the segmented mirror hsm = SegmentedMirror(aper, seg_pos) # Make a pupil plane wavefront from aperture wf = hcipy.Wavefront(aper, wavelength=wvln) ### Poppy SM psm = poppy.dms.HexSegmentedDeformableMirror(name='Poppy SM', rings=3, flattoflat=FLATTOFLAT * u.m, gap=GAPSIZE * u.m, center=False) ### Apply pistons hc_ims = [] pop_ims = [] for aber_rad in aber_array: # Flatten both SMs hsm.flatten() psm.flatten() # HCIPy for i in [19, 28]: hsm.set_segment(i, util.aber_to_opd(aber_rad, wvln) / 2, 0, 0) # Poppy for i in [34, 25]: psm.set_actuator(i, util.aber_to_opd(aber_rad, wvln) * u.m, 0, 0) # 34 in poppy is 19 in HCIPy ### Propagate to image plane ### HCIPy # Apply SM to pupil plane wf wf_fp_pistoned = hsm(wf) # Propagate from SM to image plane im_pistoned_hc = prop(wf_fp_pistoned) ### Poppy # Make an optical system with the Poppy SM and a detector osys = poppy.OpticalSystem() osys.add_pupil(psm) pxscle = 0.0031 * fac # I'm tweaking pixelscale and fov_arcsec to match the HCIPy image fovarc = 0.05 * fac osys.add_detector(pixelscale=pxscle, fov_arcsec=fovarc, oversample=10) # Calculate the PSF psf = osys.calc_psf(wvln) # Get the PSF as an array im_pistoned_pop = psf[0].data hc_ims.append(im_pistoned_hc.intensity.shaped / np.max(im_pistoned_hc.intensity)) pop_ims.append(im_pistoned_pop / np.max(im_pistoned_pop)) ### Trying to do it with numbers hc_ims = np.array(hc_ims) pop_ims = np.array(pop_ims) sum_hc = np.sum(hc_ims, axis=(1, 2)) sum_pop = np.sum( pop_ims, axis=(1, 2) ) - 1.75 # the -1.75 is just there because I didn't bother about image normalization too much plt.suptitle('Image degradation of SMs') plt.plot(aber_array, sum_hc, label='HCIPy SM') plt.plot(aber_array, sum_pop, label='Poppy SM') plt.xlabel('rad') plt.ylabel('image sum') plt.legend() plt.show()
import hcipy as hci import time as time_pkg #just nice to have ################################################################ ### SETUP ################################################################ wavelength_wfs = 842.0E-9 #meters telescope_diameter = 10.0 #meters #make pupil slightly larger than the telescope diameter to control edge effects pupil_grid_diameter = 60 / 56 * telescope_diameter num_pupil_pixels = 100 #100 pupil pixels across the pupil diameter #pupil_grid is the fundamental "what apertures get evaluated on" pupil_grid = hci.make_pupil_grid(num_pupil_pixels, pupil_grid_diameter) Hz = 1e3 ################################################################ ### FUNCTIONS ################################################################ def make_keck_ap(grid): ''' Iteratively step through the rings of keck to generate the primary mirror aperture. This currently ignores any spiders or gaps between mirrors. ''' #Find the positions where there are mirrors centroid_pos = [[0, 0]]
def __init__(self, input_dir, apod_design, samp): self.nseg = 120 self.wvln = 638e-9 # m self.diam = 15. # m self.sampling = samp self.lam_over_d = self.wvln / self.diam self.apod_dict = { 'small': { 'pxsize': 1000, 'fpm_rad': 3.5, 'fpm_px': 150, 'iwa': 3.4, 'owa': 12., 'fname': '0_LUVOIR_N1000_FPM350M0150_IWA0340_OWA01200_C10_BW10_Nlam5_LS_IDD0120_OD0982_no_ls_struts.fits' }, 'medium': { 'pxsize': 1000, 'fpm_rad': 6.82, 'fpm_px': 250, 'iwa': 6.72, 'owa': 23.72, 'fname': '0_LUVOIR_N1000_FPM682M0250_IWA0672_OWA02372_C10_BW10_Nlam5_LS_IDD0120_OD0982_no_ls_struts.fits' }, 'large': { 'pxsize': 1000, 'fpm_rad': 13.38, 'fpm_px': 400, 'iwa': 13.28, 'owa': 46.88, 'fname': '0_LUVOIR_N1000_FPM1338M0400_IWA1328_OWA04688_C10_BW10_Nlam5_LS_IDD0120_OD0982_no_ls_struts.fits' } } self.imlamD = 1.2 * self.apod_dict[apod_design]['owa'] # Pupil plane optics aper_path = 'inputs/TelAp_LUVOIR_gap_pad01_bw_ovsamp04_N1000.fits' aper_ind_path = 'inputs/TelAp_LUVOIR_gap_pad01_bw_ovsamp04_N1000_indexed.fits' apod_path = os.path.join(input_dir, 'luvoir_stdt_baseline_bw10', apod_design + '_fpm', 'solutions', self.apod_dict[apod_design]['fname']) ls_fname = 'inputs/LS_LUVOIR_ID0120_OD0982_no_struts_gy_ovsamp4_N1000.fits' pup_read = hc.read_fits(os.path.join(input_dir, aper_path)) aper_ind_read = hc.read_fits(os.path.join(input_dir, aper_ind_path)) apod_read = hc.read_fits(os.path.join(input_dir, apod_path)) ls_read = hc.read_fits(os.path.join(input_dir, ls_fname)) pupil_grid = hc.make_pupil_grid( dims=self.apod_dict[apod_design]['pxsize'], diameter=self.diam) self.aperture = hc.Field(pup_read.ravel(), pupil_grid) self.aper_ind = hc.Field(aper_ind_read.ravel(), pupil_grid) self.apod = hc.Field(apod_read.ravel(), pupil_grid) self.ls = hc.Field(ls_read.ravel(), pupil_grid) # Load segment positions from fits header hdr = fits.getheader(os.path.join(input_dir, aper_ind_path)) poslist = [] for i in range(self.nseg): segname = 'SEG' + str(i + 1) xin = hdr[segname + '_X'] yin = hdr[segname + '_Y'] poslist.append((xin, yin)) poslist = np.transpose(np.array(poslist)) self.seg_pos = hc.CartesianGrid(poslist) # Focal plane mask samp_foc = self.apod_dict[apod_design]['fpm_px'] / ( self.apod_dict[apod_design]['fpm_rad'] * 2) focal_grid_fpm = hc.make_focal_grid( pupil_grid=pupil_grid, q=samp_foc, num_airy=self.apod_dict[apod_design]['fpm_rad'], wavelength=self.wvln) self.fpm = 1 - hc.circular_aperture( 2 * self.apod_dict[apod_design]['fpm_rad'] * self.lam_over_d)(focal_grid_fpm) # Final focal plane grid (detector) self.focal_det = hc.make_focal_grid(pupil_grid=pupil_grid, q=self.sampling, num_airy=self.imlamD, wavelength=self.wvln) luvoir_params = { 'wavelength': self.wvln, 'diameter': self.diam, 'imlamD': self.imlamD, 'fpm_rad': self.apod_dict[apod_design]['fpm_rad'] } # Initialize the general segmented telescope with APLC class, includes the SM super().__init__(aper=self.aperture, indexed_aperture=self.aper_ind, seg_pos=self.seg_pos, apod=self.apod, lyotst=self.ls, fpm=self.fpm, focal_grid=self.focal_det, params=luvoir_params) # Propagators self.coro = hc.LyotCoronagraph(pupil_grid, self.fpm, self.ls) self.prop = hc.FraunhoferPropagator(pupil_grid, self.focal_det) self.coro_no_ls = hc.LyotCoronagraph(pupil_grid, self.fpm)
def clean_vapp(): import numpy as np import matplotlib.pyplot as plt from hcipy import read_fits, make_pupil_grid, make_focal_grid, FraunhoferPropagator, circular_aperture, evaluate_supersampled, imshow_field, imsave_field, Field, LyotCoronagraph, Wavefront script_path = os.path.realpath(__file__).split("vApp_reduction",1)[0] full_path = script_path+"vApp_reduction/data/" amplitude_file = full_path+'SCExAO_vAPP_amplitude_resampled.fits' phase_file = full_path+'SCExAO_vAPP_phase_resampled.fits' # loading the files required for generating the vAPP. amplitude_temp = read_fits(amplitude_file) phase_temp = read_fits(phase_file) # number of pixels along one axis in the pupil Npix = amplitude_temp.shape[0] # generating the grids pupil_grid = make_pupil_grid(Npix) focal_grid = make_focal_grid(pupil_grid, 4, 25) # Mapping from pupil plane to focal plane propagator = FraunhoferPropagator(pupil_grid, focal_grid) # converting the amplitude and phase to fields amplitude = Field(amplitude_temp.ravel(), pupil_grid) phase = Field(phase_temp.ravel(), pupil_grid) # the wavefront for the three PSFs pupil_wf_PSF_1 = Wavefront(amplitude * np.exp(1j * phase)) # coronagraphic PSF 1 pupil_wf_PSF_2 = Wavefront(amplitude * np.exp(-1j * phase)) # coronagraphic PSF 2 pupil_wf_PSF_3 = Wavefront(amplitude) # leakage # Propagating them to the focal plane focal_wf_PSF_1 = propagator(pupil_wf_PSF_1) focal_wf_PSF_2 = propagator(pupil_wf_PSF_2) focal_wf_PSF_3 = propagator(pupil_wf_PSF_3) # setting the total power in the images focal_wf_PSF_1.total_power = 1 focal_wf_PSF_2.total_power = 1 focal_wf_PSF_3.total_power = 1 # the strenght of the leakage term leakage = 0.02 # getting the power and scaling with leakage PSF_1_pwr = focal_wf_PSF_1.power * (1 - leakage / 2) PSF_2_pwr = focal_wf_PSF_2.power * (1 - leakage / 2) PSF_3_pwr = focal_wf_PSF_3.power * leakage # generating the total PSF of the vAPP total_PSF = PSF_1_pwr + PSF_2_pwr + PSF_3_pwr # normalizing to sum = 1 total_PSF /= np.sum(total_PSF) fig = plt.figure() imshow_field(np.log10(total_PSF / total_PSF.max()), vmin=-5, vmax=0) cbar = plt.colorbar() cbar.set_label(r"$^{10}\log(contrast)$") plt.ylabel(r"$\lambda/D$") plt.xlabel(r"$\lambda/D$") plt.show() output_path = get_output_path("miscellaneous") fig.savefig(output_path+"clean_vapp")
wvln = 638e-9 print('Setting up optics...') print('Data folder: {}'.format(workdir)) print('Coronagraph: {}'.format(apodizer_design)) # Create SM # Read pupil and indexed pupil inputdir = '/Users/ilaginja/Documents/LabWork/ultra/LUVOIR_delivery_May2019/' aper_path = 'inputs/TelAp_LUVOIR_gap_pad01_bw_ovsamp04_N1000.fits' aper_ind_path = 'inputs/TelAp_LUVOIR_gap_pad01_bw_ovsamp04_N1000_indexed.fits' aper_read = hc.read_fits(os.path.join(inputdir, aper_path)) aper_ind_read = hc.read_fits(os.path.join(inputdir, aper_ind_path)) # Sample them on a pupil grid and make them hc.Fields pupil_grid = hc.make_pupil_grid(dims=aper_ind_read.shape[0], diameter=15) aper = hc.Field(aper_read.ravel(), pupil_grid) aper_ind = hc.Field(aper_ind_read.ravel(), pupil_grid) # Create the wavefront on the aperture wf_aper = hc.Wavefront(aper, wvln) # Load segment positions from fits header hdr = fits.getheader(os.path.join(inputdir, aper_ind_path)) poslist = [] for i in range(nseg): segname = 'SEG' + str(i + 1) xin = hdr[segname + '_X'] yin = hdr[segname + '_Y'] poslist.append((xin, yin)) poslist = np.transpose(np.array(poslist))
def clean_vapp(): import numpy as np import matplotlib.pyplot as plt from hcipy import read_fits, make_pupil_grid, make_focal_grid, FraunhoferPropagator, circular_aperture, evaluate_supersampled, imshow_field, imsave_field, Field, LyotCoronagraph, Wavefront script_path = os.path.realpath(__file__).split("vApp_reduction", 1)[0] full_path = script_path + "vApp_reduction/data/" amplitude_file = full_path + 'SCExAO_vAPP_amplitude_resampled.fits' phase_file = full_path + 'SCExAO_vAPP_phase_resampled.fits' # loading the files required for generating the vAPP. amplitude_temp = read_fits(amplitude_file) phase_temp = read_fits(phase_file) # number of pixels along one axis in the pupil Npix = amplitude_temp.shape[0] # generating the grids pupil_grid = make_pupil_grid(Npix) focal_grid = make_focal_grid(pupil_grid, 4, 25) # Mapping from pupil plane to focal plane propagator = FraunhoferPropagator(pupil_grid, focal_grid) # converting the amplitude and phase to fields amplitude = Field(amplitude_temp.ravel(), pupil_grid) phase = Field(phase_temp.ravel(), pupil_grid) # the wavefront for the three PSFs pupil_wf_PSF_1 = Wavefront(amplitude * np.exp(1j * phase)) # coronagraphic PSF 1 pupil_wf_PSF_2 = Wavefront(amplitude * np.exp(-1j * phase)) # coronagraphic PSF 2 pupil_wf_PSF_3 = Wavefront(amplitude) # leakage # Propagating them to the focal plane focal_wf_PSF_1 = propagator(pupil_wf_PSF_1) focal_wf_PSF_2 = propagator(pupil_wf_PSF_2) focal_wf_PSF_3 = propagator(pupil_wf_PSF_3) # setting the total power in the images focal_wf_PSF_1.total_power = 1 focal_wf_PSF_2.total_power = 1 focal_wf_PSF_3.total_power = 1 # the strenght of the leakage term leakage = 0.02 # getting the power and scaling with leakage PSF_1_pwr = focal_wf_PSF_1.power * (1 - leakage / 2) PSF_2_pwr = focal_wf_PSF_2.power * (1 - leakage / 2) PSF_3_pwr = focal_wf_PSF_3.power * leakage # generating the total PSF of the vAPP total_PSF = PSF_1_pwr + PSF_2_pwr + PSF_3_pwr # normalizing to sum = 1 total_PSF /= np.sum(total_PSF) fig = plt.figure() imshow_field(np.log10(total_PSF / total_PSF.max()), vmin=-5, vmax=0) cbar = plt.colorbar() cbar.set_label(r"$^{10}\log(contrast)$") plt.ylabel(r"$\lambda/D$") plt.xlabel(r"$\lambda/D$") plt.show() output_path = get_output_path("miscellaneous") fig.savefig(output_path + "clean_vapp") fig = plt.figure() imshow_field(np.log10(total_PSF / total_PSF.max()), vmin=-5, vmax=0) bbox_props = dict(boxstyle="circle,pad=0.3", fill=False, alpha=1, fc=None, ec="red", lw=5) plt.text(10.3, 10.7, " ", ha="center", va="center", rotation=45, size=110, bbox=bbox_props) plt.text(-10.3, -10.7, " ", ha="center", va="center", rotation=45, size=110, bbox=bbox_props) bbox_props = dict(boxstyle="circle,pad=0.3", fill=False, alpha=1, fc=None, ec="white", lw=5) plt.text(0, 0, " ", ha="center", va="center", rotation=45, size=55, bbox=bbox_props) plt.text(8, -15, " ", ha="center", va="center", rotation=45, size=55, bbox=bbox_props) plt.text(-8, 15, " ", ha="center", va="center", rotation=45, size=55, bbox=bbox_props) cbar = plt.colorbar() cbar.set_label(r"$^{10}\log(contrast)$") plt.ylabel(r"$\lambda/D$") plt.xlabel(r"$\lambda/D$") plt.show() output_path = get_output_path("miscellaneous") fig.savefig(output_path + "clean_vapp_annotated")
def analytical_model(zernike_pol, coef, cali=False): """ :param zernike_pol: :param coef: :param cali: bool; True if we already have calibration coefficients to use. False if we still need to create them. :return: """ #-# Parameters dataDir = os.path.join(CONFIG_INI.get('local', 'local_data_path'), 'active') telescope = CONFIG_INI.get('telescope', 'name') nb_seg = CONFIG_INI.getint(telescope, 'nb_subapertures') tel_size_m = CONFIG_INI.getfloat(telescope, 'diameter') * u.m real_size_seg = CONFIG_INI.getfloat( telescope, 'flat_to_flat' ) # in m, size in meters of an individual segment flatl to flat size_seg = CONFIG_INI.getint( 'numerical', 'size_seg') # pixel size of an individual segment tip to tip wvln = CONFIG_INI.getint(telescope, 'lambda') * u.nm inner_wa = CONFIG_INI.getint(telescope, 'IWA') outer_wa = CONFIG_INI.getint(telescope, 'OWA') tel_size_px = CONFIG_INI.getint( 'numerical', 'tel_size_px') # pupil diameter of telescope in pixels im_size_pastis = CONFIG_INI.getint( 'numerical', 'im_size_px_pastis') # image array size in px sampling = CONFIG_INI.getfloat('numerical', 'sampling') # sampling size_px_tel = tel_size_m / tel_size_px # size of one pixel in pupil plane in m px_sq_to_rad = (size_px_tel * np.pi / tel_size_m) * u.rad zern_max = CONFIG_INI.getint('zernikes', 'max_zern') sz = CONFIG_INI.getint('numerical', 'im_size_lamD_hcipy') # Create Zernike mode object for easier handling zern_mode = util.ZernikeMode(zernike_pol) #-# Mean subtraction for piston if zernike_pol == 1: coef -= np.mean(coef) #-# Generic segment shapes if telescope == 'JWST': # Load pupil from file pupil = fits.getdata( os.path.join(dataDir, 'segmentation', 'pupil.fits')) # Put pupil in randomly picked, slightly larger image array pup_im = np.copy(pupil) # remove if lines below this are active #pup_im = np.zeros([tel_size_px, tel_size_px]) #lim = int((pup_im.shape[1] - pupil.shape[1])/2.) #pup_im[lim:-lim, lim:-lim] = pupil # test_seg = pupil[394:,197:315] # this is just so that I can display an individual segment when the pupil is 512 # test_seg = pupil[:203,392:631] # ... when the pupil is 1024 # one_seg = np.zeros_like(test_seg) # one_seg[:110, :] = test_seg[8:, :] # this is the centered version of the individual segment for 512 px pupil # Creat a mini-segment (one individual segment from the segmented aperture) mini_seg_real = poppy.NgonAperture( name='mini', radius=real_size_seg ) # creating real mini segment shape with poppy #test = mini_seg_real.sample(wavelength=wvln, grid_size=flat_diam, return_scale=True) # fix its sampling with wavelength mini_hdu = mini_seg_real.to_fits(wavelength=wvln, npix=size_seg) # make it a fits file mini_seg = mini_hdu[ 0].data # extract the image data from the fits file elif telescope == 'ATLAST': # Create mini-segment pupil_grid = hcipy.make_pupil_grid(dims=tel_size_px, diameter=real_size_seg) focal_grid = hcipy.make_focal_grid( pupil_grid, sampling, sz, wavelength=wvln.to( u.m).value) # fov = lambda/D radius of total image prop = hcipy.FraunhoferPropagator(pupil_grid, focal_grid) mini_seg_real = hcipy.hexagonal_aperture(circum_diameter=real_size_seg, angle=np.pi / 2) mini_seg_hc = hcipy.evaluate_supersampled( mini_seg_real, pupil_grid, 4 ) # the supersampling number doesn't really matter in context with the other numbers mini_seg = mini_seg_hc.shaped # make it a 2D array # Redefine size_seg if using HCIPy size_seg = mini_seg.shape[0] # Make stand-in pupil for DH array pupil = fits.getdata( os.path.join(dataDir, 'segmentation', 'pupil.fits')) pup_im = np.copy(pupil) #-# Generate a dark hole mask #TODO: simplify DH generation and usage dh_area = util.create_dark_hole( pup_im, inner_wa, outer_wa, sampling ) # this might become a problem if pupil size is not same like pastis image size. fine for now though. if telescope == 'ATLAST': dh_sz = util.zoom_cen(dh_area, sz * sampling) #-# Import information form segmentation script Projection_Matrix = fits.getdata( os.path.join(dataDir, 'segmentation', 'Projection_Matrix.fits')) vec_list = fits.getdata( os.path.join(dataDir, 'segmentation', 'vec_list.fits')) # in pixels NR_pairs_list = fits.getdata( os.path.join(dataDir, 'segmentation', 'NR_pairs_list_int.fits')) # Figure out how many NRPs we're dealing with NR_pairs_nb = NR_pairs_list.shape[0] #-# Chose whether calibration factors to do the calibraiton with if cali: filename = 'calibration_' + zern_mode.name + '_' + zern_mode.convention + str( zern_mode.index) ck = fits.getdata( os.path.join(dataDir, 'calibration', filename + '.fits')) else: ck = np.ones(nb_seg) coef = coef * ck #-# Generic coefficients # the coefficients in front of the non redundant pairs, the A_q in eq. 13 in Leboulleux et al. 2018 generic_coef = np.zeros( NR_pairs_nb ) * u.nm * u.nm # setting it up with the correct units this will have for q in range(NR_pairs_nb): for i in range(nb_seg): for j in range(i + 1, nb_seg): if Projection_Matrix[i, j, 0] == q + 1: generic_coef[q] += coef[i] * coef[j] #-# Constant sum and cosine sum - calculating eq. 13 from Leboulleux et al. 2018 if telescope == 'JWST': i_line = np.linspace(-im_size_pastis / 2., im_size_pastis / 2., im_size_pastis) tab_i, tab_j = np.meshgrid(i_line, i_line) cos_u_mat = np.zeros( (int(im_size_pastis), int(im_size_pastis), NR_pairs_nb)) elif telescope == 'ATLAST': i_line = np.linspace(-(2 * sz * sampling) / 2., (2 * sz * sampling) / 2., (2 * sz * sampling)) tab_i, tab_j = np.meshgrid(i_line, i_line) cos_u_mat = np.zeros((int((2 * sz * sampling)), int( (2 * sz * sampling)), NR_pairs_nb)) # Calculating the cosine terms from eq. 13. # The -1 with each NR_pairs_list is because the segment names are saved starting from 1, but Python starts # its indexing at zero, so we have to make it start at zero here too. for q in range(NR_pairs_nb): # cos(b_q <dot> u): b_q with 1 <= q <= NR_pairs_nb is the basis of NRPS, meaning the distance vectors between # two segments of one NRP. We can read these out from vec_list. # u is the position (vector) in the detector plane. Here, those are the grids tab_i and tab_j. # We need to calculate the dot product between all b_q and u, so in each iteration (for q), we simply add the # x and y component. cos_u_mat[:, :, q] = np.cos( px_sq_to_rad * (vec_list[NR_pairs_list[q, 0] - 1, NR_pairs_list[q, 1] - 1, 0] * tab_i) + px_sq_to_rad * (vec_list[NR_pairs_list[q, 0] - 1, NR_pairs_list[q, 1] - 1, 1] * tab_j)) * u.dimensionless_unscaled sum1 = np.sum( coef**2 ) # sum of all a_{k,l} in eq. 13 - this works only for single Zernikes (l fixed), because np.sum would sum over l too, which would be wrong. if telescope == 'JWST': sum2 = np.zeros( (int(im_size_pastis), int(im_size_pastis)) ) * u.nm * u.nm # setting it up with the correct units this will have elif telescope == 'ATLAST': sum2 = np.zeros( (int(2 * sz * sampling), int(2 * sz * sampling))) * u.nm * u.nm for q in range(NR_pairs_nb): sum2 = sum2 + generic_coef[q] * cos_u_mat[:, :, q] #-# Local Zernike if telescope == 'JWST': # Generate a basis of Zernikes with the mini segment being the support isolated_zerns = zern.hexike_basis(nterms=zern_max, npix=size_seg, rho=None, theta=None, vertical=False, outside=0.0) # Calculate the Zernike that is currently being used and put it on one single subaperture, the result is Zer # Apply the currently used Zernike to the mini-segment. if zernike_pol == 1: Zer = np.copy(mini_seg) elif zernike_pol in range(2, zern_max - 2): Zer = np.copy(mini_seg) Zer = Zer * isolated_zerns[zernike_pol - 1] # Fourier Transform of the Zernike - the global envelope mf = mft.MatrixFourierTransform() ft_zern = mf.perform(Zer, im_size_pastis / sampling, im_size_pastis) elif telescope == 'ATLAST': isolated_zerns = hcipy.make_zernike_basis(num_modes=zern_max, D=real_size_seg, grid=pupil_grid, radial_cutoff=False) Zer = hcipy.Wavefront(mini_seg_hc * isolated_zerns[zernike_pol - 1], wavelength=wvln.to(u.m).value) # Fourier transform the Zernike ft_zern = prop(Zer) #-# Final image if telescope == 'JWST': # Generating the final image that will get passed on to the outer scope, I(u) in eq. 13 intensity = np.abs(ft_zern)**2 * (sum1.value + 2. * sum2.value) elif telescope == 'ATLAST': intensity = ft_zern.intensity.shaped * (sum1.value + 2. * sum2.value) # PASTIS is only valid inside the dark hole, so we cut out only that part if telescope == 'JWST': tot_dh_im_size = sampling * (outer_wa + 3) intensity_zoom = util.zoom_cen( intensity, tot_dh_im_size ) # zoom box is (owa + 3*lambda/D) wide, in terms of lambda/D dh_area_zoom = util.zoom_cen(dh_area, tot_dh_im_size) dh_psf = dh_area_zoom * intensity_zoom elif telescope == 'ATLAST': dh_psf = dh_sz * intensity """ # Create plots. plt.subplot(1, 3, 1) plt.imshow(pupil, origin='lower') plt.title('JWST pupil and diameter definition') plt.plot([46.5, 464.5], [101.5, 409.5], 'r-') # show how the diagonal of the pupil is defined plt.subplot(1, 3, 2) plt.imshow(mini_seg, origin='lower') plt.title('JWST individual mini-segment') plt.subplot(1, 3, 3) plt.imshow(dh_psf, origin='lower') plt.title('JWST dark hole') plt.show() """ # dh_psf is the image of the dark hole only, the pixels outside of it are zero # intensity is the entire final image return dh_psf, intensity
wf = prop_through_lens(wf, -z1g, IOR1) wf.total_power = 1 return wf return _inner_, _inner_backwards if __name__ == "__main__": plt.style.use("dark_background") radius = 0.013 / 2 #5 sep = 0.12 #10 IOR = 1.48 #acrylic at 1 um res = 600 #600 seems to be the critical value pad = 1 pupil_grid = hc.make_pupil_grid(res, 2 * radius) r1, r2 = make_remapping_gauss_annulus(res, 0.23, 0, 3) z1, z2 = make_PIAA_lenses(r1 * radius, r2 * radius, IOR, IOR, sep) ## fix radius dependence here! apodizer = fresnel_apodizer(pupil_grid, sep, pad, r1, r2, z1, z2, IOR, IOR) keck_pupil_hires = np.array(fits.open("pupil_KECK_high_res.fits")[0].data, dtype=np.float32) ap_arr = resize2(keck_pupil_hires, (res, res)).flatten() ap = hc.Field(ap_arr, pupil_grid) plt.imshow(ap.real.reshape((600, 600))) plt.show() wf = PaddedWavefront(ap, wavelength=1.e-6, pad_factor=pad)
segment_flat_to_flat = (pupil_diameter - (2 * num_rings + 1) * gap_size) / (2 * num_rings + 1) focal_length = 1 # m # Parameters for the simulation num_pix = 512 wavelength = 638e-9 num_airy = 20 sampling = 4 norm = False # In[3]: # HCIPy grids and propagator pupil_grid = hcipy.make_pupil_grid(dims=num_pix, diameter=pupil_diameter) focal_grid = hcipy.make_focal_grid(sampling, num_airy, pupil_diameter=pupil_diameter, reference_wavelength=wavelength, focal_length=focal_length) focal_grid = focal_grid.shifted(focal_grid.delta / 2) prop = hcipy.FraunhoferPropagator(pupil_grid, focal_grid, focal_length) # In[4]: # Define function from rad of phase to m OPD def aber_to_opd(aber_rad, wavelength):
def __init__(self, diameter, fnum, wavelength, num_DM_acts=30, wavelength_0=None, obstr_frac=0.242): if wavelength_0 is None: wavelength_0 = wavelength self.reference_wavelength = wavelength_0 self.diameter = diameter self.fnum = fnum self.num_acts = num_DM_acts self.obstr_frac = obstr_frac self.wavelength = wavelength ## setting up low and high res pupil grids. the low res grid is used for to only calibrate/control the DM num_pupil_pixels = self.low_pupil_res pupil_pixel_samps = self.low_pupil_res * 0.95 #the keck pupil fits seems be padded with zeros around border by ~5% self.pupil_plane_res = (num_pupil_pixels, num_pupil_pixels) pupil_grid_diam = diameter * num_pupil_pixels / pupil_pixel_samps self.pupil_grid_diam = pupil_grid_diam self.pupil_sample_rate = pupil_grid_diam / num_pupil_pixels self.pupil_grid = hc.make_pupil_grid(self.low_pupil_res, diameter=pupil_grid_diam) self.pupil_grid_hires = hc.make_pupil_grid(self.hi_pupil_res, diameter=pupil_grid_diam) ## now set up the actual pupil fields keck_pupil_hires = np.array( fits.open("pupil_KECK_high_res.fits")[0].data, dtype=np.float32) ap_arr = resize2(keck_pupil_hires, (self.low_pupil_res, self.low_pupil_res)) ap_arr_hires = resize2(keck_pupil_hires, (self.hi_pupil_res, self.hi_pupil_res)) self.ap = hc.Field(ap_arr.flatten(), self.pupil_grid) self.ap_hires = hc.Field(ap_arr_hires.flatten(), self.pupil_grid_hires) ## we need to make two DMs, one sampled on the low res pupil grid and another on the hi res pupil grid act_spacing = diameter / num_DM_acts influence_funcs = hc.make_gaussian_influence_functions( self.pupil_grid, num_DM_acts, act_spacing) self.DM = hc.DeformableMirror(influence_funcs) influence_funcs_hires = hc.make_gaussian_influence_functions( self.pupil_grid_hires, num_DM_acts, act_spacing) self.DM_hires = hc.DeformableMirror(influence_funcs_hires) ## make the rest of our optics (besides PIAA/collimator) self.pwfs = hc.PyramidWavefrontSensorOptics(self.pupil_grid, wavelength_0=wavelength_0) self.detector = hc.NoiselessDetector() self.dt = 1 #integration time in seconds (?) ## focal grid set up. linear resolution in pixels is 2 * q * num_airy self.focal_grid = hc.make_focal_grid(q=16 * 2, num_airy=16, f_number=fnum, reference_wavelength=wavelength_0) self.ref_image = None self.rmat = None ## pupil -> focal and focal -> pupil propagators self.propagator = hc.FraunhoferPropagator(self.pupil_grid, self.focal_grid, focal_length=diameter * fnum) self.propagator_backward = hc.FraunhoferPropagator( self.focal_grid, self.pupil_grid, focal_length=-diameter * fnum) ## misc other stuff that is useful to cache/save self.t_arr = None self.DMshapes = None self.psgen = None self.apodize = None self.apodize_backwards = None self.collimate = None self.collimator_grid = None self.current_phase_screen = None self.wf_pupil, self.wf_pupil_hires, self.wf_focal = None, None, None self.PIAA_args, self.col_args = None, None
def get_atlast_aperture(normalized=False, with_segment_gaps=True, segment_transmissions=1, write_to_disk=False, outDir=None): """Make the ATLAST/HiCAT pupil mask. This function is a copy of make_hicat_aperture(), except that it also returns the segment positions. Parameters ---------- normalized : boolean If this is True, the outer diameter will be scaled to 1. Otherwise, the diameter of the pupil will be 15.0 meters. with_segment_gaps : boolean Include the gaps between individual segments in the aperture. segment_transmissions : scalar or array_like The transmission for each of the segments. If this is a scalar, this transmission will be used for all segments. Returns ------- Field generator The ATLAST aperture. CartesianGrid The segment positions. """ pupil_diameter = PUP_DIAMETER segment_circum_diameter = 2 / np.sqrt(3) * pupil_diameter / 7 num_rings = 3 segment_gap = CONFIG_PASTIS.getfloat(which_tel, 'gaps') if not with_segment_gaps: segment_gap = 0 if normalized: segment_circum_diameter /= pupil_diameter segment_gap /= pupil_diameter pupil_diameter = 1.0 segment_positions = hcipy.make_hexagonal_grid( segment_circum_diameter / 2 * np.sqrt(3), num_rings) segment_positions = segment_positions.subset(lambda grid: ~( hcipy.circular_aperture(segment_circum_diameter)(grid) > 0)) hexagon = hcipy.hexagonal_aperture(segment_circum_diameter - segment_gap) def segment(grid): return hexagon(grid.rotated(np.pi / 2)) segmented_aperture = hcipy.make_segmented_aperture(segment, segment_positions, segment_transmissions) def func(grid): res = segmented_aperture(grid) return hcipy.Field(res, grid) # Save pupil to disk, as pdf and fits if write_to_disk: pupil_grid = hcipy.make_pupil_grid(dims=pupil_size, diameter=pupil_diameter) atlast = hcipy.evaluate_supersampled(func, pupil_grid, 8) hcipy.imshow_field(atlast) for i in range(36): plt.annotate(str(i + 1), size='x-large', xy=(segment_positions.x[i] - pupil_diameter * 0.03, segment_positions.y[i] - pupil_diameter * 0.02)) # -0.03/-0.02 is for shifting the numbers closer to the segment centers. Scaling that by pupil_diameter # keeps them in place. plt.savefig(os.path.join(outDir, 'ATLAST_pupil.pdf')) util.write_fits(atlast.shaped, os.path.join(outDir, 'pupil.fits')) return func, segment_positions
def gen_atmos(plot=False, debug=True): """ generates atmospheric phase distortions using hcipy (updated from original using CAOS) read more on HCIpy here: https://hcipy.readthedocs.io/en/latest/index.html In hcipy, the atmosphere evolves as a function of time, specified by the user. User can thus specify the timescale of evolution through both velocity of layer and time per step in the obs_sequence, in loop for medis_main.gen_timeseries(). :param plot: turn plotting on or off :return: todo add simple mp.Pool.map code to make maps in parrallel """ if tp.use_atmos is False: pass # only make new atmosphere map if using the atmosphere else: if sp.verbose: dprint("Making New Atmosphere Model") # Saving Parameters # np.savetxt(iop.atmosconfig, ['Grid Size', 'Wvl Range', 'Number of Frames', 'Layer Strength', 'Outer Scale', 'Velocity', 'Scale Height', cp.model]) # np.savetxt(iop.atmosconfig, ['ap.grid_size', 'ap.wvl_range', 'ap.numframes', 'atmp.cn_sq', 'atmp.L0', 'atmp.vel', 'atmp.h', 'cp.model']) # np.savetxt(iop.atmosconfig, [ap.grid_size, ap.wvl_range, ap.numframes, atmp.cn_sq, atmp.L0, atmp.vel, atmp.h, cp.model], fmt='%s') wsamples = np.linspace(ap.wvl_range[0], ap.wvl_range[1], ap.n_wvl_init) wavefronts = [] ################################## # Initiate HCIpy Atmosphere Type ################################## pupil_grid = hcipy.make_pupil_grid(sp.grid_size, tp.entrance_d) if atmp.model == 'single': layers = [ hcipy.InfiniteAtmosphericLayer(pupil_grid, atmp.cn_sq, atmp.L0, atmp.vel, atmp.h, 2) ] elif atmp.model == 'hcipy_standard': # Make multi-layer atmosphere # layers = hcipy.make_standard_atmospheric_layers(pupil_grid, atmp.L0) heights = np.array([500, 1000, 2000, 4000, 8000, 16000]) velocities = np.array([10, 10, 10, 10, 10, 10]) Cn_squared = np.array( [0.2283, 0.0883, 0.0666, 0.1458, 0.3350, 0.1350]) * 3.5e-12 layers = [] for h, v, cn in zip(heights, velocities, Cn_squared): layers.append( hcipy.InfiniteAtmosphericLayer(pupil_grid, cn, atmp.L0, v, h, 2)) elif atmp.model == 'evolving': raise NotImplementedError atmos = hcipy.MultiLayerAtmosphere(layers, scintilation=False) for wavelength in wsamples: wavefronts.append( hcipy.Wavefront( hcipy.Field(np.ones(pupil_grid.size), pupil_grid), wavelength)) if atmp.correlated_sampling: # Damage Detection and Localization from Dense Network of Strain Sensors # fancy sampling goes here normal = corrsequence(sp.numframes, atmp.tau / sp.sample_time)[1] * atmp.std uniform = (special.erf(normal / np.sqrt(2)) + 1) times = np.cumsum(uniform) * sp.sample_time if debug: import matplotlib.pylab as plt plt.plot(normal) plt.figure() plt.plot(uniform) plt.figure() plt.hist(uniform) plt.figure() plt.plot( np.arange(0, sp.numframes * sp.sample_time, sp.sample_time)) plt.plot(times) plt.show() else: times = np.arange(0, sp.numframes * sp.sample_time, sp.sample_time) ########################################### # Evolving Wavefront using HCIpy tools ########################################### for it, t in enumerate(times): atmos.evolve_until(t) for iw, wf in enumerate(wavefronts): wf2 = atmos.forward(wf) filename = get_filename( it, wsamples[iw], (iop.atmosdir, sp.sample_time, atmp.model)) if sp.verbose: dprint(f"atmos file = {filename}") hdu = fits.ImageHDU( wf2.phase.reshape(sp.grid_size, sp.grid_size)) hdu.header['PIXSIZE'] = tp.entrance_d / sp.grid_size hdu.writeto(filename, overwrite=True) if plot and iw == 0: import matplotlib.pyplot as plt from medis.twilight_colormaps import sunlight plt.figure() plt.title( f"Atmosphere Phase Map t={t} lambda={eformat(wsamples[iw], 3, 2)}" ) hcipy.imshow_field(wf2.phase, cmap=sunlight) plt.colorbar() plt.show(block=True)
def gen_atmos(plot=False): """ generates atmospheric phase distortions using hcipy (updated from original using CAOS) read more on HCIpy here: https://hcipy.readthedocs.io/en/latest/index.html In hcipy, the atmosphere evolves as a function of time, specified by the user. User can thus specify the timescale of evolution through both velocity of layer and time per step in the obs_sequence, in loop for medis_main.gen_timeseries(). :param plot: turn plotting on or off :return: """ dprint("Making New Atmosphere Model") # Saving Parameters # np.savetxt(iop.atmosconfig, ['Grid Size', 'Wvl Range', 'Number of Frames', 'Layer Strength', 'Outer Scale', 'Velocity', 'Scale Height', cp.model]) # np.savetxt(iop.atmosconfig, ['ap.grid_size', 'ap.wvl_range', 'ap.numframes', 'atmp.cn_sq', 'atmp.L0', 'atmp.vel', 'atmp.h', 'cp.model']) # np.savetxt(iop.atmosconfig, [ap.grid_size, ap.wvl_range, ap.numframes, atmp.cn_sq, atmp.L0, atmp.vel, atmp.h, cp.model], fmt='%s') wsamples = np.linspace(ap.wvl_range[0], ap.wvl_range[1], ap.n_wvl_init) wavefronts = [] ################################## # Initiate HCIpy Atmosphere Type ################################## pupil_grid = hcipy.make_pupil_grid(sp.grid_size, tp.entrance_d) if atmp.model == 'single': layers = [ hcipy.InfiniteAtmosphericLayer(pupil_grid, atmp.cn_sq, atmp.L0, atmp.vel, atmp.h, 2) ] elif atmp.model == 'hcipy_standard': # Make multi-layer atmosphere layers = hcipy.make_standard_atmospheric_layers( pupil_grid, atmp.outer_scale) elif atmp.model == 'evolving': raise NotImplementedError atmos = hcipy.MultiLayerAtmosphere(layers, scintilation=False) for wavelength in wsamples: wavefronts.append( hcipy.Wavefront(hcipy.Field(np.ones(pupil_grid.size), pupil_grid), wavelength)) ########################################### # Evolving Wavefront using HCIpy tools ########################################### for it, t in enumerate( np.arange(0, sp.numframes * sp.sample_time, sp.sample_time)): atmos.evolve_until(t) for iw, wf in enumerate(wavefronts): wf2 = atmos.forward(wf) filename = get_filename(it, wsamples[iw]) dprint(f"atmos file = {filename}") hdu = fits.ImageHDU(wf2.phase.reshape(sp.grid_size, sp.grid_size)) hdu.header['PIXSIZE'] = tp.entrance_d / sp.grid_size hdu.writeto(filename, overwrite=True) if plot and iw == 0: import matplotlib.pyplot as plt from medis.twilight_colormaps import sunlight plt.figure() plt.title( f"Atmosphere Phase Map t={t} lambda={eformat(wsamples[iw], 3, 2)}" ) hcipy.imshow_field(wf2.phase, cmap=sunlight) plt.colorbar() plt.show(block=True)
print("generating: " + str(numb) + " psfs with:\n fried parameter: " + str(fried_parameter) + "\n sample time: " + str(time_between)) D_tel = 8.2 # meter wavelength = 1e-6 # meter oversampling = 8 # loading the files required for generating the vAPP. amplitude_temp = read_fits(amplitude_file) phase_temp = read_fits(phase_file) # number of pixels along one axis in the pupil Npix = amplitude_temp.shape[0] # generating the grids pupil_grid = make_pupil_grid(Npix, D_tel) focal_grid = make_focal_grid(pupil_grid, 4, 25, wavelength=wavelength) #kolmogorov: verdeling voor sterkte special freq spectral_noise_factory = SpectralNoiseFactoryFFT(kolmogorov_psd, pupil_grid, oversampling) turbulence_layers = make_standard_multilayer_atmosphere( fried_parameter=fried_parameter, wavelength=wavelength) # create phase screen atmospheric_model = AtmosphericModel(spectral_noise_factory, turbulence_layers) # Mapping from pupil plane to focal plane prop = FraunhoferPropagator(pupil_grid, focal_grid) # converting the amplitude and phase to fields amplitude = Field(amplitude_temp.ravel(), pupil_grid)