def test_inwave_fresnel(plot=False): '''Verify basic functionality of the inwave kwarg for a basic FresnelOpticalSystem()''' npix = 128 oversample = 2 # HST example - Following example in PROPER Manual V2.0 page 49. lambda_m = 0.5e-6 * u.m diam = 2.4 * u.m fl_pri = 5.52085 * u.m d_pri_sec = 4.907028205 * u.m fl_sec = -0.6790325 * u.m d_sec_to_focus = 6.3919974 * u.m m1 = poppy.QuadraticLens(fl_pri, name='Primary') m2 = poppy.QuadraticLens(fl_sec, name='Secondary') hst = poppy.FresnelOpticalSystem(pupil_diameter=diam, npix=npix, beam_ratio=1 / oversample) hst.add_optic(poppy.CircularAperture(radius=diam.value / 2)) hst.add_optic( poppy.SecondaryObscuration(secondary_radius=0.396, support_width=0.0264, support_angle_offset=45.0)) hst.add_optic(m1) hst.add_optic(m2, distance=d_pri_sec) hst.add_optic(poppy.ScalarTransmission( planetype=poppy_core.PlaneType.image, name='focus'), distance=d_sec_to_focus) if plot: plt.figure(figsize=(12, 8)) psf1, wfs1 = hst.calc_psf(wavelength=lambda_m, display_intermediates=plot, return_intermediates=True) # now test the system by inputting a wavefront first wfin = poppy.FresnelWavefront(beam_radius=diam / 2, wavelength=lambda_m, npix=npix, oversample=oversample) if plot: plt.figure(figsize=(12, 8)) psf2, wfs2 = hst.calc_psf(wavelength=lambda_m, display_intermediates=plot, return_intermediates=True, inwave=wfin) wf = wfs1[-1].wavefront wf_no_in = wfs2[-1].wavefront assert np.allclose( wf, wf_no_in ), 'Results differ unexpectedly when using inwave argument for FresnelOpticalSystem().'
def test_CompoundOpticalSystem_fresnel(npix=128, display=False): """ Test that the CompoundOpticalSystem container works for Fresnel systems Parameters ---------- npix : int Number of pixels for the pupil sampling. Kept small by default to reduce test run time. """ import poppy opt1 = poppy.SquareAperture() opt2 = poppy.CircularAperture(radius=0.55) # a single optical system osys = poppy.FresnelOpticalSystem(beam_ratio=0.25, npix=npix) osys.add_optic(opt1) osys.add_optic(opt2, distance=10 * u.cm) osys.add_optic(poppy.QuadraticLens(1.0 * u.m)) osys.add_optic(poppy.Detector(pixelscale=0.25 * u.micron / u.pixel, fov_pixels=512), distance=1 * u.m) psf = osys.calc_psf(display_intermediates=display) if display: plt.figure() # a Compound Fresnel optical system osys1 = poppy.FresnelOpticalSystem(beam_ratio=0.25, npix=npix) osys1.add_optic(opt1) osys2 = poppy.FresnelOpticalSystem(beam_ratio=0.25) osys2.add_optic(opt2, distance=10 * u.cm) osys2.add_optic(poppy.QuadraticLens(1.0 * u.m)) osys2.add_optic(poppy.Detector(pixelscale=0.25 * u.micron / u.pixel, fov_pixels=512), distance=1 * u.m) cosys = poppy.CompoundOpticalSystem([osys1, osys2]) psf2 = cosys.calc_psf(display_intermediates=display) assert np.allclose( psf[0].data, psf2[0].data ), "Results from simple and compound Fresnel systems differ unexpectedly." return psf, psf2
def test_wavefront_tilt_sign_and_direction_fresnel(plot=False, npix=128): """ Test that tilt with increasing WFE towards the +X direction moves the PSF in the -X direction Fresnel propagation version See also test_core.test_source_offsets_in_OpticalSystem """ # Create a wavefront and apply a tilt wave = poppy.FresnelWavefront(beam_radius=0.5 * u.m, npix=npix, oversample=8) wave *= poppy.CircularAperture(radius=0.5 * u.m) # tilt in arcseconds tilt_angle = -0.2 # must be a negative number (for -X direction shift), and within the FOV wave.tilt( Xangle=tilt_angle ) # for this function, the input is the desired direction for the image to tilt. # A shift to -X is implemented by creating an OPD that increases toward +X n = wave.shape[0] assert wave.wfe[n // 2, n // 2 - 5] < wave.wfe[n // 2, n // 2 + 5], "Wavefront error should increase to +X" if plot: plt.suptitle("Wavefront tilt sign test (Fresnel propagation)", fontweight='bold') wave.display(what='both') focal_length = 1 * u.m wave *= poppy.QuadraticLens(f_lens=focal_length) wave.propagate_fresnel(focal_length) if plot: plt.figure() wave.display(what='both', crosshairs=True, imagecrop=0.00001, scale='log') n = wave.shape[0] nominal_cen = n // 2 # In Fresnel mode, PSFs are centered on a pixel by default # (different from in Fraunhofer mode by half a pixel) cen = poppy.measure_centroid(wave.as_fits()) assert np.allclose( cen[0], nominal_cen), "Tilt in X should not displace the PSF in Y" assert cen[ 1] < nominal_cen, "WFE tilt increasing to +X should displace the PSF to -X" assert np.allclose( ((cen[1] - nominal_cen) * u.pixel * wave.pixelscale).to_value(u.m), ((tilt_angle * u.arcsec).to_value(u.radian) * focal_length).to_value( u.m)), "PSF offset distance did not match expected amount"
def WFIRSTSPC(npix=256, ratio=0.25): Tel_fname = os.path.join(os.environ['WEBBPSF_PATH'], "AFTA_CGI_C5_Pupil_onax_256px_flip.fits") SP_fname = os.path.join(os.environ['WEBBPSF_PATH'], "CGI/optics/CHARSPC_SP_256pix.fits.gz") FPM_fname = os.path.join( os.environ['WEBBPSF_PATH'], "CGI/optics/CHARSPC_FPM_25WA90_2x65deg_-_FP1res4_evensamp_D072_F770.fits.gz" ) LS_fname = os.path.join(os.environ['WEBBPSF_PATH'], "CGI/optics/SPC_LS_30D88_256pix.fits.gz") D_prim = 2.37 * u.m D_relay = 20 * u.mm fr_pri = 7.8 fl_pri = D_prim * fr_pri fl_m2 = fl_pri * D_relay / D_prim fr_m3 = 20. fl_m3 = fr_m3 * D_relay oversamp = 4 wfirst_optsys = poppy.FresnelOpticalSystem(pupil_diameter=D_prim, beam_ratio=ratio, npix=npix) telap = poppy.FITSOpticalElement(transmission=Tel_fname) SP = poppy.FITSOpticalElement(transmission=SP_fname) #default FPM pixelscale is in arcsecs FPM = poppy.FITSOpticalElement( transmission=FPM_fname, planetype=poppy.poppy_core.PlaneType.intermediate, pixelscale=0.005) SP.pixelscale = 0.5 * u.cm / SP.shape[0] / u.pix FPM.pixelscale = 0.5 * u.cm / SP.shape[0] / u.pix m1 = poppy.QuadraticLens(fl_pri, name='Primary') m2 = poppy.QuadraticLens(fl_m2, name='M2') m3 = poppy.QuadraticLens(fl_m3, name='M3') m4 = poppy.QuadraticLens(fl_m3, name='M4') m5 = poppy.QuadraticLens(fl_m3, name='M5') m6 = poppy.QuadraticLens(fl_m3, name='M6') wfirst_optsys.add_optic(telap) wfirst_optsys.add_optic(m1) wfirst_optsys.add_optic(m2, distance=fl_pri + fl_m2) wfirst_optsys.add_optic(m3, distance=fl_m2 + fl_m3) wfirst_optsys.add_optic(m4, distance=2 * fl_m3) wfirst_optsys.add_optic(SP, distance=fl_m3) wfirst_optsys.add_optic(m5, distance=fl_m3) wfirst_optsys.add_optic(FPM, distance=fl_m3) wfirst_optsys.add_optic(m5, distance=2 * fl_m3) wfirst_optsys.add_optic(poppy.ScalarTransmission( planetype=poppy.poppy_core.PlaneType.intermediate, name='focus', ), distance=fl_m3 + 0.39999923 * u.m) return wfirst_optsys
def test_pupil_orientations_before_and_after_focus_fresnel( plot=False, npix_pupil=128, npix_fov=128): """ Verify pupil orientations before and after focus, and signs of thin lens defocus 1. A negative weak lens produces images (before focus) that have consistent orientation with the exit pupil 2. A positive weak lens produces images (after focus) that have the opposite orientation as the exit pupil 3. Images with the same magnitude but opposite signs of defocus should be 180 degree rotations of one another. """ wave0 = poppy.FresnelWavefront(beam_radius=1.5 * u.m, oversample=2) wave0 *= poppy.LetterFAperture(radius=1 / np.sqrt(2) * u.m) focal_length = 1.0 * u.m lens = poppy.QuadraticLens(f_lens=focal_length, name="Converging lens") wave0 *= lens wave1 = wave0.copy() wave2 = wave0.copy() fov = 0.00003 wave1.propagate_fresnel(0.99999 * focal_length) wave2.propagate_fresnel(1.00001 * focal_length) if plot: fig, axes = plt.subplots(figsize=(15, 6), ncols=3) plt.suptitle("Before and After Focus sign test (Fresnel propagation)", fontweight='bold') wave0.display(ax=axes[0]) wave1.display(imagecrop=fov, title='Intensity at plane before focus', scale='log', ax=axes[1]) wave2.display(imagecrop=fov, title='Intensity at plane after focus', scale='log', ax=axes[2]) assert brighter_top_half(wave0.intensity) and brighter_left_half( wave0.intensity ), "Letter F should be brighter at top and left at pupil" assert brighter_top_half(wave1.intensity) and brighter_left_half( wave1.intensity ), "Image with negative lens (before focus) should have same orientation as the pupil " assert (not brighter_top_half(wave2.intensity)) and ( not brighter_left_half(wave2.intensity) ), "Image with positive lens (after focus) should have opposite orientation as the pupil "
print('Select desired coefficient distribution (default Sigmoid):') print('Sigmoid: 1 \nGaussian: 2 \nExponential: 3 \n') distribution = input() if distribution == 2: wf_error_budget = gaussian_budget(n_coeff) elif distribution == 3: wf_error_budget = exponential_budget(n_coeff) else: wf_error_budget = sigmoid_budget(n_coeff) #%% --------------------------------- OPTICAL ELEMENTS ---------------------------------------------- aperture = poppy.CircularAperture(radius=radius, name='Pupil') objective = poppy.QuadraticLens(fl_obj, name='Objective lens') # ----------------------------------- OPTICAL SYSTEM ------------------------------------------------ # Initialize results as lists psf_results = [] zernike_coeff = [] file_h5, group_h5 = init_h5(save_dir) for image_idx in range(n_set): osys = poppy.FresnelOpticalSystem(pupil_diameter=10 * radius, npix=256, beam_ratio=SAMPLING)
def test_CompoundOpticalSystem_hybrid(npix=128): """ Test that the CompoundOpticalSystem container works for hybrid Fresnel+Fraunhofer systems Defining "works correctly" here is a bit arbitrary given the different physical assumptions. For the purpose of this test we consider a VERY simple case, mostly a Fresnel system. We split out the first optic and put that in a Fraunhofer system. We then test that a compound hybrid system yields the same results as the original fully-Fresnel system. Parameters ---------- npix : int Number of pixels for the pupil sampling. Kept small by default to reduce test run time. """ import poppy opt1 = poppy.SquareAperture() opt2 = poppy.CircularAperture(radius=0.55) ###### Simple test case to exercise the conversion functions, with only trivial propagation osys1 = poppy.OpticalSystem() osys1.add_pupil(opt1) osys2 = poppy.FresnelOpticalSystem() osys2.add_optic(poppy.ScalarTransmission()) osys3 = poppy.OpticalSystem() osys3.add_pupil(poppy.ScalarTransmission()) osys3.add_detector(fov_pixels=64, pixelscale=0.01) cosys = poppy.CompoundOpticalSystem([osys1, osys2, osys3]) psf, ints = cosys.calc_psf(return_intermediates=True) assert len(ints) == 4, "Unexpected number of intermediate wavefronts" assert isinstance(ints[0], poppy.Wavefront), "Unexpected output type" assert isinstance(ints[1], poppy.FresnelWavefront), "Unexpected output type" assert isinstance(ints[2], poppy.Wavefront), "Unexpected output type" ###### Harder case involving more complex actual propagations #===== a single Fresnel optical system ===== osys = poppy.FresnelOpticalSystem(beam_ratio=0.25, npix=128, pupil_diameter=2 * u.m) osys.add_optic(opt1) osys.add_optic(opt2, distance=10 * u.cm) osys.add_optic(poppy.QuadraticLens(1.0 * u.m)) osys.add_optic(poppy.Detector(pixelscale=0.125 * u.micron / u.pixel, fov_pixels=512), distance=1 * u.m) #===== two systems, joined into a CompoundOpticalSystem ===== # First part is Fraunhofer then second is Fresnel osys1 = poppy.OpticalSystem(npix=128, oversample=4, name="FIRST PART, FRAUNHOFER") # Note for strict consistency we need to apply a half pixel shift to optics in the Fraunhofer part; # this accomodates the differences between different types of image centering. pixscl = osys.input_wavefront().pixelscale halfpixshift = (pixscl * 0.5 * u.pixel).to(u.m).value opt1shifted = poppy.SquareAperture(shift_x=halfpixshift, shift_y=halfpixshift) osys1.add_pupil(opt1shifted) osys2 = poppy.FresnelOpticalSystem(name='SECOND PART, FRESNEL') osys2.add_optic(opt2, distance=10 * u.cm) osys2.add_optic(poppy.QuadraticLens(1.0 * u.m)) osys2.add_optic(poppy.Detector(pixelscale=0.125 * u.micron / u.pixel, fov_pixels=512), distance=1 * u.m) cosys = poppy.CompoundOpticalSystem([osys1, osys2]) #===== PSF calculations ===== psf_simple = osys.calc_psf(return_intermediates=False) poppy.poppy_core._log.info( "******=========calculation divider============******") psf_compound = cosys.calc_psf(return_intermediates=False) np.testing.assert_allclose( psf_simple[0].data, psf_compound[0].data, err_msg= "PSFs do not match between equivalent simple and compound/hybrid optical systems" )
def csvFresnel(rx_csv, samp, oversamp, break_plane): M1_radius = rx_csv['Radius_m'][ 1] * u.m # Element [1] is M1 because Element [0] is the pupil mask sys_build = poppy.FresnelOpticalSystem(pupil_diameter=2 * M1_radius, npix=samp, beam_ratio=oversamp) # Entrance Aperture sys_build.add_optic(poppy.CircularAperture(radius=M1_radius)) # Build MagAO-X optical system from CSV file to the Lyot plane for n_optic, optic in enumerate(rx_csv): # n_optic: count, optic: value dz = optic[ 'Distance_m'] * u.m # Propagation distance from the previous optic (n_optic-1) fl = optic[ 'Focal_Length_m'] * u.m # Focal length of the current optic (n_optic) #print('Check PSD file for %s: %s' % (optic['Name'], optic['surf_PSD'])) # if PSD file present if optic['surf_PSD_filename'] != 'none': # make a string insertion for the file location surf_file_loc = optic['surf_PSD_folder'] + optic[ 'surf_PSD_filename'] + '.fits' # call surfFITS to send out surface map optic_surface = surfFITS(file_loc=surf_file_loc, optic_type=optic['optic_type'], opdunit=optic['OPD_unit'], name=optic['Name'] + ' surface') # Add generated surface map to optical system sys_build.add_optic(optic_surface, distance=dz) if fl != 0: # powered optic with PSD file present sys_build.add_optic(poppy.QuadraticLens(fl, name=optic['Name'])) # no distance; surface comes first sys_build.add_optic( poppy.CircularAperture(radius=optic['Radius_m'] * u.m, name=optic['Name'] + " aperture")) elif optic[ 'Type'] != 'pupil': # non-powered optic but has PSD present that is NOT the pupil sys_build.add_optic( poppy.CircularAperture(radius=optic['Radius_m'] * u.m, name=optic['Name'] + " aperture")) # if no PSD file present (DM, focal plane, testing optical surface) else: # if powered optic is being tested if fl != 0: sys_build.add_optic(poppy.QuadraticLens(fl, name=optic['Name']), distance=dz) sys_build.add_optic( poppy.CircularAperture(radius=optic['Radius_m'] * u.m, name=optic['Name'] + " aperture")) # for DM, flat mirrors elif optic['Type'] == 'mirror' or optic['Type'] == 'DM': sys_build.add_optic(poppy.ScalarTransmission( planetype=PlaneType.intermediate, name=optic['Name']), distance=dz) sys_build.add_optic( poppy.CircularAperture(radius=optic['Radius_m'] * u.m, name=optic['Name'] + " aperture")) else: # for focal plane, science plane, lyot plane sys_build.add_optic(poppy.ScalarTransmission( planetype=PlaneType.intermediate, name=optic['Name']), distance=dz) # if the most recent optic studied was the break plane, break out of loop. if optic['Type'] == break_plane: #print('Finish building FresnelOpticalSystem at %s' % break_plane) break return sys_build
def openspim_illumination(wavelength=500e-9, refr_index=1.333, laser_radius=1.2e-3, objective_na=0.3, objective_focal=18e-3, slit_opening=10e-3, pixelscale=635e-9, npix_fov=512, rel_thresh=None, simu_size=2048, oversample=16): """ Compute the illumination function of an OpenSPIM device Parameters ---------- wavelength : float, optional illumination wavelength in meters, by default 500e-9 refr_index : float, optional imaging medium refraction index, by default 1.333 laser_radius : float, optional source laser radius in meters, by default 1.2e-3 objective_na : float, optional illumination objective NA, by default 0.3 objective_focal : float, optional illumination objective focal length in meters, by default 18e-3 slit_opening : float, optional vertical slit opening in meters, by default 10e-3 pixelscale : float, optional target pixelscale in meters per pixel, by default 1.3e-3/2048 npix_fov : int, optional target size in pixels, by default 512 rel_thresh: float, optional relative threshold to crop the beam thickness if a full row is below this theshold, all rows after are removed will be computed as compared to the maximum pixel simu_size : int, optional size of the arrays used for simulation, by default 2048 oversample : int, optional oversampling used for the simulation (must be increased sith simu_size), by default 16 Returns ------- array [ZXY] the illumination function """ pixel_width = 1 wavelength *= u.m laser_radius *= u.m objective_focal *= u.m pixelscale *= (u.m / u.pixel) slit_opening *= u.m noop = poppy.ScalarTransmission() beam_ratio = 1 / oversample fov_pixels = npix_fov * u.pixel detector = poppy.FresnelOpticalSystem() detector.add_detector(fov_pixels=fov_pixels, pixelscale=pixelscale) # We approximate the objective aperture with a square one to make it separable # Given the shape of the wavefront, we estimate the generated error to be negligible objective_radius = math.tan(math.asin( objective_na / refr_index)) * objective_focal objective_aperture = poppy.RectangleAperture(name='objective aperture', width=2 * objective_radius, height=2 * objective_radius) objective_lens = poppy.QuadraticLens(f_lens=objective_focal, name='objective lens') obj_aperture = poppy.FresnelOpticalSystem() obj_aperture.add_optic(objective_aperture, objective_focal) # Implement the objective lens separately to be able to account for refractive index change obj_lens = poppy.FresnelOpticalSystem() obj_lens.add_optic(objective_lens) # Computed as following: going through T1 then CLens then T2 # is equivalent to going through CLens with focal/4 # Then the radius is computed as the Fourier transform of the input beam, per 2F lens system w0_y = (12.5e-3 * u.m * wavelength) / (2 * np.pi**2 * laser_radius) laser_shape_y = poppy.GaussianAperture(w=w0_y, pupil_diam=5 * w0_y) path_y = poppy.FresnelOpticalSystem(pupil_diameter=2 * w0_y, npix=pixel_width, beam_ratio=beam_ratio) path_y.add_optic(laser_shape_y) # Going through T1, slit and T2 is equivalent to going through a half-sized slit, # then propagating 1/4 the distance # Since we use 1D propagation, we can increase oversampling a lot for better results laser_shape_z = poppy.GaussianAperture(w=laser_radius, pupil_diam=slit_opening / 2) slit = poppy.RectangleAperture(name='Slit', width=slit_opening / 2, height=slit_opening / 2) path_z = poppy.FresnelOpticalSystem(pupil_diameter=slit_opening / 2, npix=pixel_width, beam_ratio=beam_ratio) path_z.add_optic(laser_shape_z) path_z.add_optic(slit) path_z.add_optic(noop, 0.25 * 100e-3 * u.m) # Propagate 1D signals wf_z = path_z.input_wavefront(wavelength=wavelength) create_wf_1d(wf_z, upsampling=simu_size) path_z.propagate(wf_z) wf_y = path_y.input_wavefront(wavelength=wavelength) create_wf_1d(wf_y, upsampling=simu_size, scale=10) path_y.propagate(wf_y) obj_aperture.propagate(wf_z) obj_aperture.propagate(wf_y) wf_z.wavelength /= refr_index wf_y.wavelength /= refr_index obj_lens.propagate(wf_z) obj_lens.propagate(wf_y) illumination = np.empty((npix_fov, npix_fov, npix_fov), dtype=wf_z.intensity.dtype) # Make sure it is centered even if pixels are odd or even offset = 0 if npix_fov % 2 else 0.5 for pix in range(npix_fov): pixel = pix - npix_fov // 2 + offset distance = pixel * pixelscale * u.pixel psf = poppy.FresnelOpticalSystem() psf.add_optic(noop, objective_focal + distance) wfc_y = wf_y.copy() wfc_z = wf_z.copy() psf.propagate(wfc_y) psf.propagate(wfc_z) resample_wavefront(wfc_y, pixelscale, fov_pixels) resample_wavefront(wfc_z, pixelscale, fov_pixels) mix = wf_mix(wfc_y, wfc_z) mix.normalize() illumination[:, pix, :] = mix.intensity if rel_thresh is not None: illumination = utils.threshold_crop(illumination, rel_thresh, 0) return illumination / illumination.sum(0).mean()
def test_FixedSamplingImagePlaneElement(display=False): poppy_tests_fpath = os.path.dirname(os.path.abspath( poppy.__file__)) + '/tests/' # HST example - Following example in PROPER Manual V2.0 page 49. # This is an idealized case and does not correspond precisely to the real telescope # Define system with units lambda_m = 0.5e-6 * u.m diam = 2.4 * u.m fl_pri = 5.52085 * u.m d_pri_sec = 4.907028205 * u.m # This is what's used in the PROPER example #d_pri_sec = 4.9069 * u.m # however Lallo 2012 gives this value, which differs slightly # from what is used in the PROPER example case. fl_sec = -0.6790325 * u.m d_sec_to_focus = 6.3916645 * u.m # place focal plane right at the beam waist after the SM fl_oap = 0.5 * u.m sampling = 2 hst = poppy.FresnelOpticalSystem(npix=128, pupil_diameter=2.4 * u.m, beam_ratio=1. / sampling) g1 = poppy.QuadraticLens(fl_pri, name='Primary', planetype=poppy.poppy_core.PlaneType.pupil) g2 = poppy.QuadraticLens(fl_sec, name='Secondary') fpm = poppy.FixedSamplingImagePlaneElement( 'BOWTIE FPM', poppy_tests_fpath + 'bowtie_fpm_0.05lamD.fits') oap = poppy.QuadraticLens(fl_oap, name='OAP') hst.add_optic(poppy.CircularAperture(radius=diam.value / 2)) hst.add_optic(g1) hst.add_optic(g2, distance=d_pri_sec) hst.add_optic(fpm, distance=d_sec_to_focus) hst.add_optic(oap, distance=fl_oap) hst.add_optic(oap, distance=fl_oap) hst.add_optic(poppy.ScalarTransmission( planetype=poppy.poppy_core.PlaneType.intermediate, name='Image'), distance=fl_oap) # Create a PSF if display: fig = plt.figure(figsize=(10, 5)) psf, waves = hst.calc_psf(wavelength=lambda_m, display_intermediates=display, return_intermediates=True) # still have to do comparison of arrays psf_result = fits.open(poppy_tests_fpath + 'FITSFPMElement_test_result.fits') psf_result_data = psf_result[0].data psf_result_pxscl = psf_result[0].header['PIXELSCL'] psf_result.close() np.testing.assert_allclose( psf[0].data, psf_result_data, rtol=1e-6, err_msg="PSF of this test does not match the saved result.", verbose=True) np.testing.assert_allclose( waves[-1].pixelscale.value, psf_result_pxscl, err_msg="PSF pixelscale of this test does not match the saved result.", verbose=True)
def test_fresnel_noninteger_oversampling(display_intermediates=False): '''Test for noninteger oversampling for basic FresnelOpticalSystem() using HST example system''' lambda_m = 0.5e-6 * u.m # lambda_m = np.linspace(0.475e-6, 0.525e-6, 3) * u.m diam = 2.4 * u.m fl_pri = 5.52085 * u.m d_pri_sec = 4.907028205 * u.m fl_sec = -0.6790325 * u.m d_sec_to_focus = 6.3919974 * u.m m1 = poppy.QuadraticLens(fl_pri, name='Primary') m2 = poppy.QuadraticLens(fl_sec, name='Secondary') image_plane = poppy.ScalarTransmission( planetype=poppy_core.PlaneType.image, name='focus') npix = 128 oversample1 = 2 hst1 = poppy.FresnelOpticalSystem(pupil_diameter=diam, npix=npix, beam_ratio=1 / oversample1) hst1.add_optic(poppy.CircularAperture(radius=diam.value / 2)) hst1.add_optic( poppy.SecondaryObscuration(secondary_radius=0.396, support_width=0.0264, support_angle_offset=45.0)) hst1.add_optic(m1) hst1.add_optic(m2, distance=d_pri_sec) hst1.add_optic(image_plane, distance=d_sec_to_focus) if display_intermediates: plt.figure(figsize=(12, 8)) psf1 = hst1.calc_psf(wavelength=lambda_m, display_intermediates=display_intermediates) # now test the second system which has a different oversampling factor oversample2 = 2.0 hst2 = poppy.FresnelOpticalSystem(pupil_diameter=diam, npix=npix, beam_ratio=1 / oversample2) hst2.add_optic(poppy.CircularAperture(radius=diam.value / 2)) hst2.add_optic( poppy.SecondaryObscuration(secondary_radius=0.396, support_width=0.0264, support_angle_offset=45.0)) hst2.add_optic(m1) hst2.add_optic(m2, distance=d_pri_sec) hst2.add_optic(image_plane, distance=d_sec_to_focus) if display_intermediates: plt.figure(figsize=(12, 8)) psf2 = hst2.calc_psf(wavelength=lambda_m, display_intermediates=display_intermediates) # Now test a 3rd HST system with oversample of 2.5 and compare to hardcoded result oversample3 = 2.5 hst3 = poppy.FresnelOpticalSystem(pupil_diameter=diam, npix=npix, beam_ratio=1 / oversample3) hst3.add_optic(poppy.CircularAperture(radius=diam.value / 2)) hst3.add_optic( poppy.SecondaryObscuration(secondary_radius=0.396, support_width=0.0264, support_angle_offset=45.0)) hst3.add_optic(m1) hst3.add_optic(m2, distance=d_pri_sec) hst3.add_optic(image_plane, distance=d_sec_to_focus) if display_intermediates: plt.figure(figsize=(12, 8)) psf3 = hst3.calc_psf(wavelength=lambda_m, display_intermediates=display_intermediates) assert np.allclose( psf1[0].data, psf2[0].data ), 'PSFs with oversampling 2 and 2.0 are surprisingly different.' np.testing.assert_almost_equal( psf3[0].header['PIXELSCL'], 0.017188733797782272, decimal=7, err_msg= 'pixelscale for the PSF with oversample of 2.5 is surprisingly different from expected result.' )
f = 20 * u.mm coefficients_sequence = [0.0, 0.0, 0.0, 0, 0.0, 0, 0.0, 0.0, 0.0] * u.um osys = poppy.FresnelOpticalSystem(pupil_diameter=2 * radius, npix=256, beam_ratio=0.25) osys.add_optic(poppy.CircularAperture(radius=radius, name='pupil')) # The system pupil zernikewfe = poppy.ZernikeWFE(radius=radius, coefficients=coefficients_sequence) osys.add_optic(zernikewfe) m1 = poppy.QuadraticLens(f, name='objective lens') osys.add_optic(m1, distance=f) #osys.add_optic(poppy.CircularAperture(radius=diam/2, name='aperture0'), distance = 0*u.mm) #m2 = poppy.QuadraticLens(fl_sec, name='Secondary') #osys.add_optic(m2, distance=d_pri_sec) #osys.add_optic(poppy.ScalarTransmission(name='free space'), distance=f1); delta = 8 * u.um osys.add_detector(pixelscale=0.1 * u.um / u.pixel, fov_pixels=256, distance=f + delta) osys.describe()
def csvFresnel(rx_csv, samp, oversamp, axis, break_plane, source_ZWFE_coeff, irisAOmap, irisAOstatus): EP_radius = rx_csv['Radius_m'][ 2] * u.m # Element [2] is IrisAO because Element [0] is the diverging source, [1] is OAP1 irisAO_radius = rx_csv['Radius_m'][6] * u.m sys_build = poppy.FresnelOpticalSystem(pupil_diameter=2 * EP_radius, npix=samp, beam_ratio=oversamp) #sys_build = poppy.OpticalSystem(pupil_diameter=2*EP_radius, npix=samp, beam_ratio=oversamp) # Entrance Aperture sys_build.add_optic(poppy.CircularAperture(radius=EP_radius)) # Apply if off-axis LGS is used if axis == 'LGS': src_aberr = poppy.ZernikeWFE(radius=irisAO_radius.value, coefficients=source_ZWFE_coeff) sys_build.add_optic(src_aberr) # if the on-axis target is used, then a source aberration will not be applied (hence on-axis and at infinity) # Build LGS optical system from CSV file to the break plane for n_optic, optic in enumerate(rx_csv): # n_optic: count, optic: value #print('n_optic = ', n_optic) dz = optic[ 'Distance_m'] * u.m # Propagation distance from the previous optic (n_optic-1) fl = optic[ 'Focal_Length_m'] * u.m # Focal length of the current optic (n_optic) #print('Check PSD file for %s: %s' % (optic['Name'], optic['surf_PSD'])) # if PSD file present if optic['surf_PSD_filename'] != 'none': # make a string insertion for the file location surf_file_loc = optic['surf_PSD_folder'] + optic[ 'surf_PSD_filename'] + '.fits' # call surfFITS to send out surface map optic_surface = mf.surfFITS(file_loc=surf_file_loc, optic_type=optic['optic_type'], opdunit=optic['OPD_unit'], name=optic['Name'] + ' surface') # Add generated surface map to optical system sys_build.add_optic(optic_surface, distance=dz) if fl != 0: # powered optic with PSD file present sys_build.add_optic(poppy.QuadraticLens(fl, name=optic['Name'])) # no distance; surface comes first sys_build.add_optic( poppy.CircularAperture(radius=optic['Radius_m'] * u.m, name=optic['Name'] + " aperture")) elif optic[ 'Type'] != 'pupil': # non-powered optic but has PSD present that is NOT the pupil sys_build.add_optic( poppy.CircularAperture(radius=optic['Radius_m'] * u.m, name=optic['Name'] + " aperture")) # if no PSD file present (DM, focal plane, testing optical surface) else: #print('Enter no PSD file condition') # if powered optic is being tested if fl != 0: #print('Enter powered optic condition') sys_build.add_optic(poppy.QuadraticLens(fl, name=optic['Name']), distance=dz) if n_optic > 0: sys_build.add_optic( poppy.CircularAperture(radius=optic['Radius_m'] * u.m, name=optic['Name'] + " aperture")) # if building IrisAO segmented DM elif optic['Name'] == 'IrisAO': #print('Enter build IrisAO map') if irisAOstatus == True: #print('IrisAO present') #sys_build.add_optic(poppy.MultiHexagonAperture(name='IrisAO DM', rings=3, side=7e-4, gap=7e-6, center=True), distance=dz) sys_build.add_optic(irisAOmap) else: #print('IrisAO not present') sys_build.add_optic(poppy.ScalarTransmission( planetype=PlaneType.intermediate, name=optic['Name']), distance=dz) sys_build.add_optic( poppy.CircularAperture(radius=optic['Radius_m'] * u.m, name=optic['Name'] + " aperture")) # for DM, flat mirrors elif optic['Type'] == 'mirror' or optic['Type'] == 'DM': #print('Enter mirror or DM conditon') sys_build.add_optic(poppy.ScalarTransmission( planetype=PlaneType.intermediate, name=optic['Name']), distance=dz) sys_build.add_optic( poppy.CircularAperture(radius=optic['Radius_m'] * u.m, name=optic['Name'] + " aperture")) else: # for focal plane, science plane, lyot plane #print('Enter focal plane conditon') if optic['Type'] == 'fplane': # Apply focal plane correction distance dz = optic['Distance_m'] * u.m + optic['Correction_m'] * u.m sys_build.add_optic(poppy.ScalarTransmission( planetype=PlaneType.intermediate, name=optic['Name']), distance=dz) # if the most recent optic studied was the break plane, break out of loop. if optic['Name'] == break_plane: #print('Finish building FresnelOpticalSystem at %s' % break_plane) break return sys_build