示例#1
0
    def get_perfect_wfs(self, wl=None):
        """compute and save perfect pupil plane and focal plane wavefronts"""

        if wl is None:
            wf_pupil = hc.Wavefront(self.ap, wavelength=self.wavelength)
            wf_pupil_hires = hc.Wavefront(self.ap_hires,
                                          wavelength=self.wavelength)
        else:
            wf_pupil = hc.Wavefront(self.ap, wavelength=wl)
            wf_pupil_hires = hc.Wavefront(self.ap_hires, wavelength=wl)

        wf_pupil.total_power = 1
        wf_pupil_hires.total_power = 1

        wf_focal = wf_pupil_hires.copy()

        if self.collimate is not None:
            wf_focal = self.collimate(wf_focal)

        if self.apodize is not None:
            wf_focal = self.apodize(wf_focal)

        wf_focal = self.propagator.forward(wf_focal)
        # note that the total power in the focal plane is not 1. some power gets deflected
        # away due to turbulence.

        self.wf_pupil, self.wf_pupil_hires, self.wf_focal = wf_pupil, wf_pupil_hires, wf_focal
        return wf_pupil, wf_pupil_hires, wf_focal
示例#2
0
def build_PYWFS(grid,
                aperture,
                DM=None,
                basis=None,
                wavelength=wavelength_wfs):
    '''
    Builds the Pyramid, the camera reading the pyramid, and 
    optionally the control matrix for a DM
    
    OUTPUTS:
    pywfs: the pyramid that you pass a wavefront to to generate 4 pupils
    camera_py: the camera that reads out the pywfs output
    img_ref: the reference image of a flat wavefront
    IM_prime: the matrix that converts pywfs measurements to DM actuator space
    '''
    pywfs = hci.PyramidWavefrontSensorOptics(grid, wavelength_0=wavelength)
    camera_py = hci.NoiselessDetector(pywfs.output_grid)

    wf = hci.Wavefront(aperture, wavelength)
    wf.total_power = 1
    camera_py.integrate(pywfs.forward(wf), 1)
    img_ref = camera_py.read_out()
    img_ref /= img_ref.sum()

    if DM is None:
        return pywfs, camera_py, img_ref, None
    else:
        IM_prime = build_IM(pywfs, camera_py, img_ref, wf, DM, basis)
        return pywfs, camera_py, img_ref, IM_prime
示例#3
0
    def calibrate_DM(self, rcond=0.1, fname_append=None, force_new=False):
        """Calculate and save the imagae reconstruction matrix for the AO system"""
        from os import path

        if fname_append is not None:
            fname = "rmat_" + str(
                self.DM.num_actuators) + "_" + str(rcond).replace(
                    ".", "") + "_" + fname_append
        else:
            fname = "rmat_" + str(
                self.DM.num_actuators) + "_" + str(rcond).replace(".", "")

        #first we make a reference image for an unaberrated wavefront passing through the pwfs
        wf = hc.Wavefront(self.ap, wavelength=self.reference_wavelength)

        wf.total_power = 1

        wf_pwfs = self.pwfs.forward(wf)

        self.ref_image = self.read_out(wf_pwfs, poisson=False)

        if path.exists(fname + ".npy") and not force_new:
            print("trying to load reconstruction matrix from " + fname)
            rmat = np.load(fname + str(".npy"))
            print("loaded cached rmat")
        else:
            print("computing reconstruction matrix")
            #compute the interaction matrix relating incoming abberations to wfs response
            probe_disp = 0.01 * self.wavelength
            slopes = []

            for i in range(self.DM.num_actuators):
                printProgressBar(i, self.DM.num_actuators)

                slope = 0

                for s in (-1, 1):
                    disps = np.zeros((self.DM.num_actuators, ))
                    disps[i] = s * probe_disp
                    self.DM.actuators = disps

                    wf_dm = self.DM.forward(wf)  #pass through DM
                    wf_dm_pwfs = self.pwfs.forward(wf_dm)  #pass through wfs

                    image = self.read_out(wf_dm_pwfs)
                    slope += s * (image - self.ref_image) / (2 * probe_disp)

                slopes.append(slope)

            print("matrix inversion...")
            basis = hc.ModeBasis(slopes)
            rmat = hc.inverse_tikhonov(basis.transformation_matrix,
                                       rcond=rcond,
                                       svd=None)
            np.save(fname, rmat)

            self.DM.actuators = np.zeros((self.DM.num_actuators, ))

        self.rmat = rmat
        return rmat
示例#4
0
 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)
示例#5
0
def score_matrix3(aberrations, show=False):
    
    # Make segmented mirror
    aper, segments = hcipy.make_hexagonal_segmented_aperture(num_rings,
                                                         segment_flat_to_flat,
                                                         gap_size,
                                                         starting_ring=1,
                                                         return_segments=True)

    aper = hcipy.evaluate_supersampled(aper, pupil_grid, 1)
    segments = hcipy.evaluate_supersampled(segments, pupil_grid, 1)
    
    # Instantiate the segmented mirror
    hsm = hcipy.SegmentedDeformableMirror(segments)

    # Make a pupil plane wavefront from aperture
    wf = hcipy.Wavefront(aper, wavelength)
    
    hsm.flatten()
    
    # Get poke segments
    for i in range(0,len(aberrations)):
        if float(aberrations[i]) != 0:
            hsm.set_segment_actuators(i, aber_to_opd(aber_rad, wavelength) / (1/float(aberrations[i])), 0, 0)
    
    if show:
        plt.figure(figsize=(8,8))
        plt.title('OPD for HCIPy SM')
        hcipy.imshow_field(hsm.surface * 2, mask=aper, cmap='RdBu_r', vmin=-5e-7, vmax=5e-7)
        plt.colorbar()
        plt.show()
    
    ### PROPAGATE AND SCORE ###
    wf_fp_pistoned = hsm(wf)

    # Propagate from SM to image plane
    im_pistoned_hc = prop(wf_fp_pistoned)
    norm_hc = np.max(im_pistoned_hc.intensity)
    
    if show:
        # Display intensity in image plane

        hcipy.imshow_field(np.log10(im_pistoned_hc.intensity / norm_hc), cmap='inferno', vmin=-9)
        plt.title('Image plane after SM')
        plt.colorbar()
        plt.show()
        
        hcipy.imshow_field(np.log10((im_pistoned_hc.intensity / norm_hc)*(annulus)), cmap='inferno', vmin=-9)
        plt.title('Annular image plane region')
        plt.colorbar()
        plt.show()
        
    interested_field = (im_pistoned_hc.intensity / norm_hc)*(1-annulus)
    score = float(hcipy.Field.sum(interested_field)/hcipy.Field.sum(im_pistoned_hc.intensity / norm_hc))*100
    
    return score
示例#6
0
 def __init__(self, aper, indexed_aperture, seg_pos, apod, lyotst, fpm,
              focal_grid, params):
     self.sm = SegmentedMirror(indexed_aperture=indexed_aperture,
                               seg_pos=seg_pos)
     self.aper = aper
     self.apodizer = apod
     self.lyotstop = lyotst
     self.fpm = fpm
     self.wvln = params['wavelength']
     self.diam = params['diameter']
     self.imlamD = params['imlamD']
     self.fpm_rad = params['fpm_rad']
     self.lamDrad = self.wvln / self.diam
     self.coro = hc.LyotCoronagraph(indexed_aperture.grid, fpm, lyotst)
     self.prop = hc.FraunhoferPropagator(indexed_aperture.grid, focal_grid)
     self.coro_no_ls = hc.LyotCoronagraph(indexed_aperture.grid, fpm)
     self.wf_aper = hc.Wavefront(aper, wavelength=self.wvln)
     self.focal_det = focal_grid
示例#7
0
def coupling_vs_r_pupilplane_single(tele: AOtele.AOtele, pupilfields, rcore,
                                    ncore, nclad, wl0):
    k0 = 2 * np.pi / wl0

    fieldshape = pupilfields[0].shape

    #need to generate the available modes
    V = LPmodes.get_V(k0, rcore, ncore, nclad)
    modes = LPmodes.get_modes(V)
    lpfields = []

    for mode in modes:
        if mode[0] == 0:
            lpfields.append(
                normalize(
                    LPmodes.lpfield(xg, yg, mode[0], mode[1], rcore, wl0,
                                    ncore, nclad)))

        else:
            lpfields.append(
                normalize(
                    LPmodes.lpfield(xg, yg, mode[0], mode[1], rcore, wl0,
                                    ncore, nclad, "cos")))
            lpfields.append(
                normalize(
                    LPmodes.lpfield(xg, yg, mode[0], mode[1], rcore, wl0,
                                    ncore, nclad, "sin")))

    lppupilfields = []

    #then backpropagate
    for field in lpfields:
        wf = hc.Wavefront(hc.Field(field.flatten(), tele.focalgrid),
                          wavelength=wl0 * 1.e-6)
        pupil_wf = tele.back_propagate(wf, True)
        lppupilfields.append(pupil_wf.electric_field.reshape(fieldshape))

    lppupilfields = np.array(lppupilfields)
    pupilfields = np.array(pupilfields)

    #compute total overlap
    powers = np.sum(pupilfields * lppupilfields, axes=(1, 2))

    return np.mean(powers), np.stddev(powers)
示例#8
0
        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
示例#9
0
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()
示例#10
0
 def gen_wf(self, wl=None):
     if wl is None:
         wl = self.wavelength
     return hc.Wavefront(self.ap, wl)
示例#11
0
def make_wf(phase, aper, wavelength=wavelength_wfs):
    wf = hci.Wavefront(np.exp(1j * phase) * aper, wavelength)
    return wf
示例#12
0
    # 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))
    seg_pos = hc.CartesianGrid(poslist)

    # Instantiate SM
    sm = SegmentedMirror(aper_ind, seg_pos)
示例#13
0
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)
示例#14
0
def compute_coupling_vs_wl_parallel(tele, DMshapes, ts, wls, wl0, rcore0,
                                    fname):
    """ compute avg coupling vs wavelength. Since psfs change with wavelength, 
        the psfs are generated on the fly from saved DM positions and atmospheric 
        turbulence states """

    #to ensure accurate psf generation, an AOtele object identical to the one used to generate DMshapes must be passed in.

    ncores = 8

    ## set up parallel stuff
    client = Client(threads_per_worker=1, n_workers=ncores)

    # get a perfect psf for Strehl calculations
    wf_pupil = hc.Wavefront(tele.ap, wavelength=wl0 * 1.e-6)
    wf_focus_perfect = tele.propagator.forward(wf_pupil)
    u_focus_perfect = AOtele.get_u(wf_focus_perfect)

    out = []

    #psf focal plane physical size
    w = None

    j = 0

    import copy

    for i in range(len(ts)):

        if i % 100 == 0 and i != 0:
            print("psfs completed: ", j)

            tele.atmos.t = ts[i]
            tele.DM.actuators = DMshapes[i]

            # we need to select a psf to do F# opt
            # by default let's just use #100, the first psf we analyze
            if i == 100:

                u = tele.get_PSF(wf_pupil)
                print("first psf @ 1um has Strehl",
                      AOtele.get_strehl(u, u_focus_perfect))
                plt.imshow(np.abs(u))
                plt.show()

                ncore = get_IOR(wl0)

                V = LPmodes.get_V(2 * np.pi / wl0, rcore0, ncore,
                                  ncore - 5.5e-3)
                modes = LPmodes.get_modes(V)

                r, p, focal_plane_width = scale_u0_all(u, modes, rcore0, ncore,
                                                       nclad, wl0)
                print('optimal psf size: ', focal_plane_width)

            couplings = []

            # most likely can be sped up. I don't know how to parallelize the psf creation since the tele object can't be shared between threads.
            for wl in wls:
                wf = hc.Wavefront(tele.ap, wavelength=wl * 1.e-6)
                u = tele.get_PSF(wf)
                c = dask.delayed(compute_coupling_at_wl)(u, focal_plane_width,
                                                         rcore0, wl)
                couplings.append(c)

            futures = dask.persist(*couplings)
            results = np.array(dask.compute(*futures))
            out.append(results)

            j += 1

    out = np.array(out)

    # save to file

    ncores = get_IOR(wls)
    with h5py.File(fname + ".hdf5", "w") as f:
        f.create_dataset("coupling", data=out)
        f.create_dataset("rcore0", data=rcore0)
        f.create_dataset("ncores", data=ncores)
        f.create_dataset("nclads", data=ncores - 5.5e-3)
        f.create_dataset("wls", data=wls)
        f.create_dataset("wl0", data=wl0)
        f.create_dataset("w", data=focal_plane_width)

    # show prelim results

    avgs = np.mean(out, axis=0)
    stds = np.std(out, axis=0)

    plt.plot(wls, avgs, color='k', ls='None', marker='.')

    plt.fill_between(wls, avgs - stds, avgs + stds, color='0.75', alpha=0.5)

    plt.axvspan(1.02 - 0.06,
                1.02 + 0.06,
                alpha=0.35,
                color="burlywood",
                zorder=-100)
    plt.axvspan(1.220 - 0.213 / 2,
                1.220 + 0.213 / 2,
                alpha=0.25,
                color="indianred",
                zorder=-100)

    plt.xlim(0.9, 1.4)
    plt.ylim(0., 1.0)
    plt.xlabel("wavelength (um)")
    plt.ylabel("coupling efficiency")
    plt.show()
示例#15
0
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)
示例#16
0
    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
示例#17
0
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