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_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_inwave_fraunhofer(plot=False): '''Verify basic functionality of the inwave kwarg for a basic OpticalSystem()''' npix = 128 oversample = 2 diam = 2.4 * u.m lambda_m = 0.5e-6 * u.m # calculate the Fraunhofer diffraction pattern hst = poppy.OpticalSystem(pupil_diameter=diam, npix=npix, oversample=oversample) hst.add_pupil(poppy.CircularAperture(radius=diam.value / 2)) hst.add_pupil( poppy.SecondaryObscuration(secondary_radius=0.396, support_width=0.0264, support_angle_offset=45.0)) hst.add_image( poppy.ScalarTransmission(planetype=poppy_core.PlaneType.image, name='focus')) if plot: plt.figure(figsize=(9, 3)) 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.Wavefront(wavelength=lambda_m, npix=npix, diam=diam, oversample=oversample) if plot: plt.figure(figsize=(9, 3)) 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 in OpticalSystem().'
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 _get_optical_system(self, fft_oversample=2, detector_oversample=None, fov_arcsec=2.8, fov_pixels=None, options=dict()): """ Return an OpticalSystem instance corresponding to the instrument as currently configured. When creating such an OpticalSystem, you must specify the parameters needed to define the desired sampling, specifically the oversampling and field of view. Parameters ---------- fft_oversample : int Oversampling factor for intermediate plane calculations. Default is 2 detector_oversample: int, optional By default the detector oversampling is equal to the intermediate calculation oversampling. If you wish to use a different value for the detector, set this parameter. Note that if you just want images at detector pixel resolution you will achieve higher fidelity by still using some oversampling (i.e. *not* setting `oversample_detector=1`) and instead rebinning down the oversampled data. fov_pixels : float Field of view in pixels. Overrides fov_arcsec if both set. fov_arcsec : float Field of view, in arcseconds. Default is 2 options : dict Other arbitrary options for optical system creation Returns ------- osys : poppy.OpticalSystem an optical system instance representing the desired configuration. """ if detector_oversample is None: detector_oversample = fft_oversample #poppy_core._log.debug("Oversample: %d %d " % (fft_oversample, detector_oversample)) optsys = poppy.OpticalSystem(name=self.name, oversample=fft_oversample) if 'source_offset_r' in options.keys(): optsys.source_offset_r = options['source_offset_r'] if 'source_offset_theta' in options.keys(): optsys.source_offset_theta = options['source_offset_theta'] optsys.npix = self.npix #---- set pupil intensity pupil_optic = GeminiPrimary(undersized=self._undersized_secondary) #if self._undersized_secondary: #pupil_optic.obscuration_diameter = 1.02375 # SM outer diameter (vs inner hole projected diameter) #---- set pupil OPD if isinstance(self.pupilopd, str): # simple filename full_opd_path = self.pupilopd if os.path.exists( self.pupilopd) else os.path.join(self._datapath, "OPD", self.pupilopd) elif hasattr(self.pupilopd, '__getitem__') and isinstance( self.pupilopd[0], basestring): # tuple with filename and slice full_opd_path = (self.pupilopd[0] if os.path.exists( self.pupilopd[0]) else os.path.join( self._datapath, "OPD", self.pupilopd[0]), self.pupilopd[1]) elif isinstance(self.pupilopd, fits.HDUList): # OPD supplied as FITS HDUList object full_opd_path = self.pupilopd # not a path per se but this works correctly to pass it to poppy elif self.pupilopd is None: full_opd_path = None else: raise TypeError( "Not sure what to do with a pupilopd of that type:" + str(type(self.pupilopd))) #---- apply pupil intensity and OPD to the optical model optsys.add_pupil(name='Gemini Primary', optic=pupil_optic, opd=full_opd_path, opdunits='micron', rotation=self._rotation) if self.dms: optsys.add_pupil(optic=self.woofer) optsys.add_pupil(optic=self.tweeter) # GPI Apodizer apod = GPI_Apodizer(name=self.apodizer, satspots=self.satspots) optsys.add_pupil(optic=apod) if self._display_before: optsys.add_image(optic=poppy.ScalarTransmission(name='Before FPM', transmission=1)) # GPI FPM fpm = GPI_FPM(name=self.occulter) optsys.add_image(optic=fpm) if self._display_before: optsys.add_pupil(optic=poppy.ScalarTransmission(name='Before Lyot', transmission=1)) # GPI Lyot Mask lyot = GPI_LyotMask(name=self.lyotmask, tabs=self.lyot_tabs) optsys.add_pupil(optic=lyot) #--- add the detector element. if fov_pixels is None: fov_pixels = np.round(fov_arcsec / self.pixelscale) if 'parity' in self.options.keys(): if self.options['parity'].lower() == 'odd' and np.remainder( fov_pixels, 2) == 0: fov_pixels += 1 if self.options['parity'].lower() == 'even' and np.remainder( fov_pixels, 2) == 1: fov_pixels += 1 optsys.add_detector(self.pixelscale, fov_pixels=fov_pixels, oversample=detector_oversample, name=self.name + " lenslet array") return optsys
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.' )
poppy.fresnel.ConicLens(f_lens=m1_fl, K=m1_conic, radius=m1_rad, name="Primary mirror"), 0 * u.m) # Secondary mirror toliman.add_optic( poppy.fresnel.ConicLens(f_lens=m2_fl, K=m2_conic, radius=m2_rad, name="Secondary mirror"), m1_m2_dist) # Aperture in primary toliman.add_optic(poppy.CircularAperture(radius=m1_aperture_rad), m1_m2_dist) toliman.add_optic(poppy.ScalarTransmission( planetype=poppy.fresnel.PlaneType.image, name='focus'), distance=m2_focus_dist) poppy.describe() # values = rosette.sample(npix=2048) # evaluate on 512 x 512 grid # fig=plt.figure(figsize=(10,5)) # rosette.display(what='both') # display phase and amplitude transmission; # pylab.savefig('test.png') # plt.close(fig) # plt.show(block=False) # npix = 1024 # ~512 is minimum to accurately recover the central diffraction spike # wf = poppy.FresnelWavefront(0.5*u.m,wavelength=2200e-9,npix=npix,oversample=4) # wf *= poppy.CircularAperture(radius=0.5)
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