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 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]] radials = [1, 2, 3] in_seg_len = 10 / 7 #meters from one flat edge of a mirror segment to the other ang_types = np.arange(30, 360, 60) + 120 for rad in radials: cur_x = rad * in_seg_len * np.cos(30 * np.pi / 180) cur_y = rad * in_seg_len * np.sin(30 * np.pi / 180) for ang in ang_types: for iii in range(rad): cur_x += in_seg_len * np.cos(ang * np.pi / 180) cur_y += in_seg_len * np.sin(ang * np.pi / 180) centroid_pos.append([cur_x, cur_y]) #Now put mirrors on each of those points out_seg_len = 20 * np.sqrt( 3) / 21 #size of the circle enclosing the hexagons oversamp = 10 keck_aperture = -5 * hci.evaluate_supersampled(hci.circular_aperture(2.4), grid, oversamp) for cent in centroid_pos: aper = hci.hexagonal_aperture(out_seg_len, angle=np.pi / 6, center=cent) heck_aperture += hci.evaluate_supersampled(aper, grid, oversamp) keck_aperture[keck_aperture < 0] = 0 keck_aperture[keck_aperture > 1] = 1 #hci.imshow_field(keck_aperture) #plt.xlabel('x position(m)') #plt.ylabel('y position(m)') #plt.colorbar() #plt.show() return keck_aperture
def contrast_luvoir_num(apodizer_choice, matrix_dir, matrix_mode='luvoir', rms=1 * u.nm): """ Compute the contrast for a random SM mislignment on the LUVOIR simulator. :param matrix_dir: str, directory of saved matrix :param matrix_mode: str, analytical or numerical; currently only numerical supported :param rms: astropy quantity, rms wfe to be put randomly on the SM :return: 2x float, E2E and matrix contrast """ # Keep track of time start_time = time.time() # runtime currently is around ? min # Parameters nb_seg = CONFIG_INI.getint('LUVOIR', 'nb_subapertures') sampling = 4 # Import numerical PASTIS matrix for HiCAT sim filename = 'PASTISmatrix_num_piston_Noll1' matrix_pastis = fits.getdata(os.path.join(matrix_dir, filename + '.fits')) # Create random aberration coefficients aber = np.random.random([nb_seg]) # piston values in input units print('PISTON ABERRATIONS:', aber) # Normalize to the RMS value I want rms_init = util.rms(aber) aber *= rms.value / rms_init calc_rms = util.rms(aber) * u.nm aber *= u.nm # making sure the aberration has the correct units print("Calculated RMS:", calc_rms) # Remove global piston aber -= np.mean(aber) # Coronagraph parameters # The LUVOIR STDT delivery in May 2018 included three different apodizers # we can work with, so I will implement an easy way of making a choice between them. design = apodizer_choice optics_input = '/Users/ilaginja/Documents/LabWork/ultra/LUVOIR_delivery_May2019/' # Instantiate LUVOIR telescope with APLC luvoir = LuvoirAPLC(optics_input, design, sampling) ### BASELINE PSF - NO ABERRATIONS, NO CORONAGRAPH # and coro PSF without aberrations start_e2e = time.time() print('Generating baseline PSF from E2E - no coronagraph, no aberrations') print('Also generating coro PSF without aberrations') psf_perfect, ref = luvoir.calc_psf(ref=True) normp = np.max(ref) psf_coro = psf_perfect / normp print('Calculating E2E contrast...') # Put aberrations on segmented mirror for nseg in range(nb_seg): luvoir.set_segment(nseg + 1, aber[nseg].to(u.m).value / 2, 0, 0) psf_luvoir = luvoir.calc_psf() psf_luvoir /= normp # Create DH dh_outer = hc.circular_aperture(2 * luvoir.apod_dict[design]['owa'] * luvoir.lam_over_d)(luvoir.focal_det) dh_inner = hc.circular_aperture(2 * luvoir.apod_dict[design]['iwa'] * luvoir.lam_over_d)(luvoir.focal_det) dh_mask = (dh_outer - dh_inner).astype('bool') # Get the mean contrast dh_intensity = psf_luvoir * dh_mask contrast_luvoir = np.mean(dh_intensity[np.where(dh_intensity != 0)]) end_e2e = time.time() ### # Calculate baseline contrast baseline_dh = psf_coro * dh_mask contrast_base = np.mean(baseline_dh[np.where(baseline_dh != 0)]) print('Baseline contrast: {}'.format(contrast_base)) ## MATRIX PASTIS print('Generating contrast from matrix-PASTIS') start_matrixpastis = time.time() # Get mean contrast from matrix PASTIS contrast_matrix = util.pastis_contrast( aber, matrix_pastis ) + contrast_base # calculating contrast with PASTIS matrix model end_matrixpastis = time.time() ## Outputs print('\n--- CONTRASTS: ---') print('Mean contrast from E2E:', contrast_luvoir) print('Contrast from matrix PASTIS:', contrast_matrix) print('\n--- RUNTIMES: ---') print('E2E: ', end_e2e - start_e2e, 'sec =', (end_e2e - start_e2e) / 60, 'min') print('Matrix PASTIS: ', end_matrixpastis - start_matrixpastis, 'sec =', (end_matrixpastis - start_matrixpastis) / 60, 'min') end_time = time.time() runtime = end_time - start_time print('Runtime for contrast_calculation_simple.py: {} sec = {} min'.format( runtime, runtime / 60)) return contrast_luvoir, contrast_matrix
def num_matrix_luvoir(design): """ Generate a numerical PASTIS matrix for a LUVOIR A coronagraph. All inputs are read from the (local) configfile and saved to the specified output directory. The LUVOIR STDT delivery in May 2018 included three different apodizers we can work with, so I will implement an easy way of making a choice between them. small, medium and large """ # Keep track of time start_time = time.time() # runtime is currently around 150 minutes print('Building numerical matrix for LUVOIR\n') ### Parameters # System parameters resDir = os.path.join(CONFIG_INI.get('local', 'local_data_path'), 'active', 'matrix_numerical') zern_number = CONFIG_INI.getint('calibration', 'zernike') zern_mode = util.ZernikeMode( zern_number) # Create Zernike mode object for easier handling # General telescope parameters nb_seg = CONFIG_INI.getint('LUVOIR', 'nb_subapertures') wvln = CONFIG_INI.getfloat('LUVOIR', 'lambda') * 1e-9 # m diam = CONFIG_INI.getfloat('LUVOIR', 'diameter') # m nm_aber = CONFIG_INI.getfloat('calibration', 'single_aberration') * 1e-9 # m # Image system parameters im_lamD = 30 # image size in lambda/D sampling = 4 # Print some of the defined parameters print('LUVOIR apodizer design: {}'.format(design)) print() print('Wavelength: {} m'.format(wvln)) print('Telescope diameter: {} m'.format(diam)) print('Number of segments: {}'.format(nb_seg)) print() print('Image size: {} lambda/D'.format(im_lamD)) print('Sampling: {} px per lambda/D'.format(sampling)) ### Setting up the paths # If subfolder "matrix_numerical" doesn't exist yet, create it. if not os.path.isdir(resDir): os.mkdir(resDir) # If subfolder "OTE_images" doesn't exist yet, create it. if not os.path.isdir(os.path.join(resDir, 'OTE_images')): os.mkdir(os.path.join(resDir, 'OTE_images')) # If subfolder "psfs" doesn't exist yet, create it. if not os.path.isdir(os.path.join(resDir, 'psfs')): os.mkdir(os.path.join(resDir, 'psfs')) ### Instantiate Luvoir telescope with chosen apodizer design optics_input = '/Users/ilaginja/Documents/LabWork/ultra/LUVOIR_delivery_May2019/' luvoir = LuvoirAPLC(optics_input, design, sampling) ### Dark hole mask dh_outer = hc.circular_aperture(2 * luvoir.apod_dict[design]['owa'] * luvoir.lam_over_d)(luvoir.focal_det) dh_inner = hc.circular_aperture(2 * luvoir.apod_dict[design]['iwa'] * luvoir.lam_over_d)(luvoir.focal_det) dh_mask = (dh_outer - dh_inner).astype('bool') ### Reference images for contrast normalization and coronagraph floor unaberrated_coro_psf, ref = luvoir.calc_psf(ref=True, display_intermediate=False, return_intermediate=False) norm = np.max(ref) dh_intensity = unaberrated_coro_psf / norm * dh_mask contrast_floor = np.mean(dh_intensity[np.where(dh_intensity != 0)]) print(contrast_floor) ### Generating the PASTIS matrix and a list for all contrasts matrix_direct = np.zeros([nb_seg, nb_seg]) # Generate empty matrix all_psfs = [] all_contrasts = [] print('nm_aber: {} m'.format(nm_aber)) for i in range(nb_seg): for j in range(nb_seg): print('\nSTEP: {}-{} / {}-{}'.format(i + 1, j + 1, nb_seg, nb_seg)) # Put aberration on correct segments. If i=j, apply only once! luvoir.flatten() luvoir.set_segment(i + 1, nm_aber / 2, 0, 0) if i != j: luvoir.set_segment(j + 1, nm_aber / 2, 0, 0) print('Calculating coro image...') image, inter = luvoir.calc_psf(ref=False, display_intermediate=False, return_intermediate='intensity') # Normalize PSF by reference image psf = image / norm # Save image to disk filename_psf = 'psf_' + zern_mode.name + '_' + zern_mode.convention + str( zern_mode.index) + '_segs_' + str(i + 1) + '-' + str(j + 1) hc.write_fits(psf, os.path.join(resDir, 'psfs', filename_psf + '.fits')) all_psfs.append(psf) # Save OPD images for testing (are these actually surface images, not OPD?) opd_name = 'opd_' + zern_mode.name + '_' + zern_mode.convention + str( zern_mode.index) + '_segs_' + str(i + 1) + '-' + str(j + 1) plt.clf() hc.imshow_field(inter['seg_mirror'], mask=luvoir.aperture, cmap='RdBu') plt.savefig(os.path.join(resDir, 'OTE_images', opd_name + '.pdf')) print('Calculating mean contrast in dark hole') dh_intensity = psf * dh_mask contrast = np.mean(dh_intensity[np.where(dh_intensity != 0)]) print('contrast:', contrast) all_contrasts.append(contrast) # Fill according entry in the matrix and subtract baseline contrast matrix_direct[i, j] = contrast - contrast_floor # Transform saved lists to arrays all_psfs = np.array(all_psfs) all_contrasts = np.array(all_contrasts) # Filling the off-axis elements matrix_two_N = np.copy( matrix_direct ) # This is just an intermediary copy so that I don't mix things up. matrix_pastis = np.copy( matrix_direct) # This will be the final PASTIS matrix. for i in range(nb_seg): for j in range(nb_seg): if i != j: matrix_off_val = (matrix_two_N[i, j] - matrix_two_N[i, i] - matrix_two_N[j, j]) / 2. matrix_pastis[i, j] = matrix_off_val print('Off-axis for i{}-j{}: {}'.format( i + 1, j + 1, matrix_off_val)) # Normalize matrix for the input aberration - the whole code is set up to be normalized to 1 nm, and even if # the units entered are in m for the sake of HCIPy, everything else is assuming the baseline is 1nm, so the # normalization can be taken out if we're working with exactly 1 nm for the aberration, even if entered in meters. #matrix_pastis /= np.square(nm_aber) # Save matrix to file filename_matrix = 'PASTISmatrix_num_' + zern_mode.name + '_' + zern_mode.convention + str( zern_mode.index) hc.write_fits(matrix_pastis, os.path.join(resDir, filename_matrix + '.fits')) print('Matrix saved to:', os.path.join(resDir, filename_matrix + '.fits')) # Save the PSF image *cube* as well (as opposed to each one individually) hc.write_fits( all_psfs, os.path.join(resDir, 'psfs', 'psf_cube' + '.fits'), ) np.savetxt(os.path.join(resDir, 'contrasts.txt'), all_contrasts, fmt='%e') # Tell us how long it took to finish. end_time = time.time() print('Runtime for matrix_building.py:', end_time - start_time, 'sec =', (end_time - start_time) / 60, 'min') print('Data saved to {}'.format(resDir))
# Instantiate SM sm = SegmentedMirror(aper_ind, seg_pos) # Instantiate LUVOIR optics_input = '/Users/ilaginja/Documents/LabWork/ultra/LUVOIR_delivery_May2019/' luvoir = LuvoirAPLC(optics_input, apodizer_design, sampling) # Generate reference PSF and coronagraph baseline luvoir.flatten() psf_unaber, ref = luvoir.calc_psf(ref=True, display_intermediate=False) norm = ref.max() #plt.show() # Make dark hole mask dh_outer = hc.circular_aperture( 2 * luvoir.apod_dict[apodizer_design]['owa'] * luvoir.lam_over_d)( luvoir.focal_det) dh_inner = hc.circular_aperture( 2 * luvoir.apod_dict[apodizer_design]['iwa'] * luvoir.lam_over_d)( luvoir.focal_det) dh_mask = (dh_outer - dh_inner).astype('bool') # plt.figure() # plt.subplot(1, 3, 1) # hc.imshow_field(dh_mask) # plt.subplot(1, 3, 2) # hc.imshow_field(psf_unaber, norm=LogNorm(), mask=dh_mask) # plt.subplot(1, 3, 3) # hc.imshow_field(psf_unaber, norm=LogNorm()) # plt.show()
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 calc_psf(self, ref=False, display_intermediate=False, return_intermediate=None): """Calculate the PSF of the segmented telescope, normalized to contrast units. Parameters: ---------- ref : bool Keyword for additionally returning the refrence PSF without the FPM. display_intermediate : bool Keyword for display of all planes. return_intermediate : string Either 'intensity', return the intensity in all planes; except phase on the SM (first plane) or 'efield', return the E-fields in all planes. Default none. Returns: -------- wf_im_coro.intensity : Field Coronagraphic image, normalized to contrast units by max of reference image (even when ref not returned). wf_im_ref.intensity : Field, optional Reference image without FPM. intermediates : dict of Fields, optional Intermediate plane intensity images; except for full wavefront on segmented mirror. wf_im_coro : Wavefront Wavefront in last focal plane. wf_im_ref : Wavefront, optional Wavefront of reference image without FPM. intermediates : dict of Wavefronts, optional Intermediate plane E-fields; except intensity in focal plane after FPM. """ # Create fake FPM for plotting fpm_plot = 1 - hc.circular_aperture(2 * self.fpm_rad * self.lamDrad)( self.focal_det) # Create apodozer as hc.Apodizer() object to be able to propagate through it apod_prop = hc.Apodizer(self.apodizer) # Calculate all wavefronts of the full propagation wf_sm = self.sm(self.wf_aper) wf_apod = apod_prop(wf_sm) wf_lyot = self.coro(wf_apod) wf_im_coro = self.prop(wf_lyot) # Wavefronts in extra planes wf_before_fpm = self.prop(wf_apod) int_after_fpm = np.log10( wf_before_fpm.intensity / wf_before_fpm.intensity.max() ) * fpm_plot # this is the intensity straight wf_before_lyot = self.coro_no_ls(wf_apod) # Wavefronts of the reference propagation wf_ref_pup = hc.Wavefront(self.apodizer * self.lyotstop, wavelength=self.wvln) wf_im_ref = self.prop(wf_ref_pup) # Display intermediate planes if display_intermediate: plt.figure(figsize=(15, 15)) plt.subplot(331) hc.imshow_field(wf_sm.phase, mask=self.aper, cmap='RdBu') plt.title('Seg aperture phase') plt.subplot(332) hc.imshow_field(wf_apod.intensity, cmap='inferno') plt.title('Apodizer') plt.subplot(333) hc.imshow_field(wf_before_fpm.intensity / wf_before_fpm.intensity.max(), norm=LogNorm(), cmap='inferno') plt.title('Before FPM') plt.subplot(334) hc.imshow_field(int_after_fpm / wf_before_fpm.intensity.max(), cmap='inferno') plt.title('After FPM') plt.subplot(335) hc.imshow_field(wf_before_lyot.intensity / wf_before_lyot.intensity.max(), norm=LogNorm(vmin=1e-3, vmax=1), cmap='inferno') plt.title('Before Lyot stop') plt.subplot(336) hc.imshow_field(wf_lyot.intensity / wf_lyot.intensity.max(), norm=LogNorm(vmin=1e-3, vmax=1), cmap='inferno', mask=self.lyotstop) plt.title('After Lyot stop') plt.subplot(337) hc.imshow_field(wf_im_coro.intensity / wf_im_ref.intensity.max(), norm=LogNorm(vmin=1e-10, vmax=1e-3), cmap='inferno') plt.title('Final image') plt.colorbar() if return_intermediate == 'intensity': # Return the intensity in all planes; except phase on the SM (first plane) intermediates = { 'seg_mirror': wf_sm.phase, 'apod': wf_apod.intensity, 'before_fpm': wf_before_fpm.intensity / wf_before_fpm.intensity.max(), 'after_fpm': int_after_fpm / wf_before_fpm.intensity.max(), 'before_lyot': wf_before_lyot.intensity / wf_before_lyot.intensity.max(), 'after_lyot': wf_lyot.intensity / wf_lyot.intensity.max() } if ref: return wf_im_coro.intensity, wf_im_ref.intensity, intermediates else: return wf_im_coro.intensity, intermediates if return_intermediate == 'efield': # Return the E-fields in all planes; except intensity in focal plane after FPM intermediates = { 'seg_mirror': wf_sm, 'apod': wf_apod, 'before_fpm': wf_before_fpm, 'after_fpm': int_after_fpm, 'before_lyot': wf_before_lyot, 'after_lyot': wf_lyot } if ref: return wf_im_coro, wf_im_ref, intermediates else: return wf_im_coro, intermediates if ref: return wf_im_coro.intensity, wf_im_ref.intensity return wf_im_coro.intensity
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)