示例#1
0
 def set_phase_map(self, z_coef, rho_zern=1., PV_rescale=True, PV_goal=50):
     """
     Generates a phase map based on Zernike polynomials
     This usually represents an NCPA map
     Uses the fast methods implemented in zern_core.py
     :param z_coef: coefficient of the Zernike series expansion
     :param PV_goal: desired Peak-To-Valley [nm] of the phase map
     """
     # Construct the base phase map
     self.zern_model = zern.ZernikeNaive(mask=self.pupil_mask)
     phase_map = self.zern_model(coef=z_coef,
                                 rho=self.rho_m / rho_zern,
                                 theta=self.theta_m,
                                 normalize_noll=False,
                                 mode='Jacobi',
                                 print_option='Silent')
     if PV_rescale:
         # Compute the current PV and rescale the coefficients
         current_pv = np.max(phase_map[np.nonzero(phase_map)]) - np.min(
             phase_map[np.nonzero(phase_map)])
         # Remember that at this point the phase map is a 1D array (a masked 2D)
         phase_map = zern.rescale_phase_map(phase_map, peak=PV_goal / 2)
         self.ncpa_coef = self.zern_model.coef * (PV_goal / current_pv
                                                  )  # Save the coefficients
         self.phase_map = zern.invert_mask(phase_map, self.pupil_mask)
     else:
         self.phase_map = zern.invert_mask(phase_map, self.pupil_mask)
         self.ncpa_coef = self.zern_model.coef
示例#2
0
 def callback_function(self, coef):
     """
     Callback to print intermediate results at each iteration
     """
     cost_now = self.cost(coef)
     self.cost_array.append(cost_now)
     coef_copy = coef
     coef_copy[0] = 0  #remove piston
     print('\nAt iteration %d :' % self.counter)
     r = self.rho_m * (self.rho_zern / self.rho_aper)
     nominal_map = self.zern_model(coef=coef_copy,
                                   rho=r,
                                   theta=self.theta_m,
                                   normalize_noll=False,
                                   mode='Jacobi',
                                   print_option='Silent')
     nominal_map = zern.invert_mask(nominal_map, self.pupil_mask)
     try:
         p0 = self.true_phase
         PV = compute_PV_2maps(phase_ref=self.true_phase,
                               phase_guess=nominal_map)
         self.PV_array.append(PV)
         RMS = compute_rms((self.true_phase - nominal_map)[self.pupil_mask])
         self.RMS_array.append(RMS)
         print('Merit Function: %.3E' % cost_now)
         print('PV : %.3f' % PV)
         print('RMS: %.3f' % RMS)
     except AttributeError:
         pass
     # cost_at_iter = self.cost(coef)
     # print 'Merit function = %e' %cost_at_iter
     self.guesses[:, :, self.counter] = nominal_map
     self.counter += 1
def rand_zern(randker):
    N = 48
    N_zern = 10
    rho_max = 0.9
    eps_rho = 1.4
    randgen = RandomState(randker)
    extents = [-1, 1, -1, 1]

    # Construct the coordinates
    x = np.linspace(-rho_max, rho_max, N)
    rho_spacing = x[1] - x[0]
    xx, yy = np.meshgrid(x, x)
    rho = np.sqrt(xx ** 2 + yy ** 2)
    theta = np.arctan2(xx, yy)
    aperture_mask = rho <= eps_rho * rho_max
    rho, theta = rho[aperture_mask], theta[aperture_mask]
    rho_max = np.max(rho)
    extends = [-rho_max, rho_max, -rho_max, rho_max]

    # Compute the Zernike series
    coef = randgen.normal(size=N_zern)
    z = zern.ZernikeNaive(mask=aperture_mask)
    phase_map = z(coef=coef, rho=rho, theta=theta, normalize_noll=False, mode='Jacobi', print_option='Silent')
    phase_map = zern.rescale_phase_map(phase_map, peak=1)
    phase_2d = zern.invert_mask(phase_map, aperture_mask)

    return phase_2d
示例#4
0
 def set_true_phase(self, ncpa_coef):
     r = self.rho_m * (self.rho_zern / self.rho_aper)
     nominal_map = self.zern_model(coef=ncpa_coef,
                                   rho=r,
                                   theta=self.theta_m,
                                   normalize_noll=False,
                                   mode='Jacobi',
                                   print_option='Silent')
     self.true_phase = zern.invert_mask(nominal_map, self.pupil_mask)
示例#5
0
    def evaluate_phase(self, zern_coef):

        _phase = self.zern_model(coef=zern_coef,
                                 rho=self.rho,
                                 theta=self.theta,
                                 normalize_noll=False,
                                 mode='Jacobi',
                                 print_option='Silent')
        phase = zern.invert_mask(_phase, self.aper_mask)

        return phase
示例#6
0
 def evaluate_phase(self, zern_coef):
     r = self.rho_m * (self.rho_zern / self.rho_aper)
     nominal_map = self.zern_model(coef=zern_coef,
                                   rho=r,
                                   theta=self.theta_m,
                                   normalize_noll=False,
                                   mode='Jacobi',
                                   print_option='Silent')
     nominal_map = zern.invert_mask(nominal_map, self.pupil_mask)
     # plt.figure()
     # plt.imshow(nominal_map)
     # plt.colorbar()
     return nominal_map
示例#7
0
 def callback_function(self, coef):
     """
     Callback to print intermediate results at each iteration
     """
     cost_now = self.cost(coef)
     self.cost_array.append(cost_now)
     grad = self.grad_analytic(coef)
     coef_copy = coef
     print('\nAt iteration %d :' % self.counter)
     print('Merit Function: %.3E' % cost_now)
     print('Grad Norm: %e' % (np.linalg.norm(grad)))
     nominal_map = self.zern_model(coef=coef_copy,
                                   rho=self.rho,
                                   theta=self.theta,
                                   normalize_noll=False,
                                   mode='Jacobi',
                                   print_option='Silent')
     nominal_map = zern.invert_mask(nominal_map, self.aper_mask)
     self.guesses[:, :, self.counter] = nominal_map
     self.counter += 1
示例#8
0
    def set_phase_diversity(self, n=2, m=0, rho_zern=1., ratio=10):
        """
        Creates the Phase Diversity map which will be used to 'defocus'
        the images. Although the common thing is to use a pure defocus term (n=2, m=0)
        any Zernike polynomial is possible

        The Phase Diversity map is rescaled according to a desired PV which is
        'ratio' times the PV of the NCPA
        """
        diversity = self.zern_model.Z_nm(n=n,
                                         m=m,
                                         rho=self.rho_m / rho_zern,
                                         theta=self.theta_m,
                                         normalize_noll=False,
                                         mode='Jacobi')
        pv_diversity = np.max(diversity) - np.min(diversity)
        pv_phase = np.max(self.phase_map) - np.min(self.phase_map)
        phase_diversity = (ratio * pv_phase) * (diversity / pv_diversity)
        # Remember that at this point the phase diversity is a 1D array (a masked 2D)
        self.phase_diversity = zern.invert_mask(phase_diversity,
                                                self.pupil_mask)
示例#9
0
    def cost(self, zern_coef):
        norm_pix = 1. / (self.N_pix)
        r = self.rho_m * (self.rho_zern / self.rho_aper)
        nominal_map = self.zern_model(coef=zern_coef,
                                      rho=r,
                                      theta=self.theta_m,
                                      normalize_noll=False,
                                      mode='Jacobi',
                                      print_option='Silent')
        nominal_map = zern.invert_mask(nominal_map, self.pupil_mask)

        # Nominal Image
        pupil_nominal = complex_function(self.pupil_mask, nominal_map,
                                         self.wave)
        propagated_nominal = norm_pix * fftshift(fft2(pupil_nominal))
        image_nominal = self.t_exp * (np.abs(propagated_nominal))**2
        J_nominal = (self.image_nominal - image_nominal)**2

        # + Phase Diversity
        pupil_plus_defocus = complex_function(
            self.pupil_mask, (nominal_map + self.phase_diversity), self.wave)
        propagated_plus_defocus = norm_pix * fftshift(fft2(pupil_plus_defocus))
        image_plus_defocus = self.t_exp * (np.abs(propagated_plus_defocus))**2
        J_plus = (self.image_plus_defocus - image_plus_defocus)**2

        if self.N_images == 3:
            # - Phase Diversity
            pupil_minus_defocus = complex_function(
                self.pupil_mask, (nominal_map - self.phase_diversity),
                self.wave)
            propagated_minus_defocus = norm_pix * fftshift(
                fft2(pupil_minus_defocus))
            image_minus_defocus = self.t_exp * (
                np.abs(propagated_minus_defocus))**2
            J_minus = (self.image_minus_defocus - image_minus_defocus)**2
            return np.sum(J_nominal + J_plus + J_minus) / (self.t_exp**2)
        else:
            return np.sum(J_nominal + J_plus) / (self.t_exp**2)
示例#10
0
rho, theta = rho[aperture_mask], theta[aperture_mask]
rho_max = np.max(rho)
extends = [-rho_max, rho_max, -rho_max, rho_max]

# Compute the Zernike series
coef = randgen.normal(size=N_zern)
z = zern.ZernikeNaive(mask=aperture_mask)
phase_map = z(coef=coef,
              rho=rho,
              theta=theta,
              normalize_noll=False,
              mode='Jacobi',
              print_option=None)

# phase_map = zern.rescale_phase_map(phase_map, peak=1)
phase_2d = zern.invert_mask(phase_map, aperture_mask)

# Introduce some noise in the map
noised_phase_map = phase_map + 0.5 * np.random.normal(size=phase_map.shape[0])
noised_2d = zern.invert_mask(noised_phase_map, aperture_mask)

plt.figure()
plt.imshow(phase_2d, extent=extends, cmap='jet')
plt.title("Zernike Series (%d polynomials)" % N_zern)
plt.xlabel('x')
plt.ylabel('y')
plt.colorbar()

plt.figure()
plt.imshow(noised_2d, extent=extends, cmap='jet')
plt.title("Zernike Series with Noise")
def reshape_model_matrix(matrix, mask):
    # Reshape the Zernike model matrix from flattened to square
    H = [zern.invert_mask(matrix[:, i], mask) for i in range(matrix.shape[-1])]
    H = np.array(H)
    H_new = np.moveaxis(H, 0, -1)
    return H_new
示例#12
0
    def grad_analytic(self, zern_coef):
        """
        Analytic gradient for speed purposes
        """
        grad_start = timer()

        N_zern = zern_coef.shape[0]

        wave_factor = 2 * np.pi / self.wave * 1j
        norm_pix = 1. / (self.N_pix)
        r = self.rho_m * (self.rho_zern / self.rho_aper)
        nominal_map = self.zern_model(coef=zern_coef,
                                      rho=r,
                                      theta=self.theta_m,
                                      normalize_noll=False,
                                      mode='Jacobi',
                                      print_option='Silent')
        nominal_map = zern.invert_mask(nominal_map, self.pupil_mask)

        # Nominal Image
        pupil_nominal = complex_function(self.pupil_mask, nominal_map,
                                         self.wave)
        propagated_nominal = norm_pix * fftshift(fft2(pupil_nominal))
        image_nominal = self.t_exp * (np.abs(propagated_nominal))**2

        # + Phase Diversity
        pupil_plus_defocus = complex_function(
            self.pupil_mask, (nominal_map + self.phase_diversity), self.wave)
        propagated_plus_defocus = norm_pix * fftshift(fft2(pupil_plus_defocus))
        image_plus_defocus = self.t_exp * (np.abs(propagated_plus_defocus))**2

        # - Phase Diversity
        pupil_minus_defocus = complex_function(
            self.pupil_mask, (nominal_map - self.phase_diversity), self.wave)
        propagated_minus_defocus = norm_pix * fftshift(
            fft2(pupil_minus_defocus))
        image_minus_defocus = self.t_exp * (
            np.abs(propagated_minus_defocus))**2

        base_factor_nom = 2 * (self.image_nominal - image_nominal)
        base_factor_plus = 2 * (self.image_plus_defocus - image_plus_defocus)
        base_factor_minus = 2 * (self.image_minus_defocus -
                                 image_minus_defocus)

        # Helper stuff
        Ec_nom, Ec_plus, Ec_minus = np.conj(pupil_nominal), np.conj(
            pupil_plus_defocus), np.conj(pupil_minus_defocus)

        FE_nom = norm_pix * fftshift(fft2(pupil_nominal))
        FE_plus = norm_pix * fftshift(fft2(pupil_plus_defocus))
        FE_minus = norm_pix * fftshift(fft2(pupil_minus_defocus))

        FEc_nom, FEc_plus, FEc_minus = np.conj(FE_nom), np.conj(
            FE_plus), np.conj(FE_minus)

        # print('Common: %f sec' %(timer() - grad_start))

        g = np.zeros(N_zern)
        for k in range(N_zern):
            Z_k = self.model_matrix[:, :, k]

            fourier_factor_nom = self.helper_grad(Z_k, pupil_nominal, Ec_nom,
                                                  FE_nom, FEc_nom)
            fourier_factor_plus = self.helper_grad(Z_k, pupil_plus_defocus,
                                                   Ec_plus, FE_plus, FEc_plus)
            fourier_factor_minus = self.helper_grad(Z_k, pupil_minus_defocus,
                                                    Ec_minus, FE_minus,
                                                    FEc_minus)

            grad_nom = np.sum(base_factor_nom * self.t_exp *
                              fourier_factor_nom)
            grad_plus = np.sum(base_factor_plus * self.t_exp *
                               fourier_factor_plus)
            grad_minus = np.sum(base_factor_minus * self.t_exp *
                                fourier_factor_minus)
            g[k] = np.real(grad_nom + grad_plus + grad_minus) / (self.t_exp**2)

        return g
示例#13
0
    print('\nML model guesses:')
    print(guessed[0, :])
    print('\nTrue values are :')
    print(true_coef)
    """ Compare the guess and the true NCPA """
    _true_ncpa = np.dot(H_matrix, zern_coef)
    guessed_coef = np.zeros_like(zern_coef)
    guessed_coef[1:] = guessed
    _guessed_ncpa = np.dot(H_matrix, guessed_coef)
    _res_ncpa = _true_ncpa - _guessed_ncpa
    PV = np.max(_res_ncpa) - np.min(_res_ncpa)
    mu = np.mean(_res_ncpa)
    nn = _res_ncpa.shape[0]
    RMS = np.sqrt(1. / nn * np.sum((_res_ncpa - mu)**2))

    true_ncpa = 1e3 * zern.invert_mask(_true_ncpa, pupil_mask)
    guessed_ncpa = 1e3 * zern.invert_mask(_guessed_ncpa, pupil_mask)
    res_ncpa = true_ncpa - guessed_ncpa

    print('\nDefocus = %.1f [mm]' % defocus)
    print('PV residual NPCA = %.1f [nm]' % (1e3 * PV))
    print('RMS residual NPCA = %.1f [nm]\n' % (1e3 * RMS))

    plt.figure()
    plt.imshow(true_ncpa, cmap='jet')
    plt.colorbar()
    plt.title('True NCPA map [nm]')

    plt.figure()
    plt.imshow(guessed_ncpa, cmap='jet')
    plt.colorbar()
示例#14
0
    plt.subplot(122)
    plt.imshow(pop.crop_array(image, N_pix), extent=extends, cmap='jet')
    plt.xlabel('X [mm]')
    plt.title('Ideal PSF')

    # Show an example of NCPA map
    zern_model = zern.ZernikeNaive(mask=aper_mask)
    zern_coef = np.zeros(10)
    zern_coef[4] = 0.25
    zz = zern_model(coef=zern_coef,
                    rho=rho,
                    theta=theta,
                    mode='Standard',
                    print_option=None)
    phase = zern.invert_mask(zz, aper_mask)

    H = zern_model.model_matrix

    plt.figure()
    plt.imshow(zern.invert_mask(H[:, 4], aper_mask), extent=[-1, 1, -1, 1])
    plt.xlim([-1.1 * eps, 1.1 * eps])
    plt.ylim([-1.1 * eps, 1.1 * eps])
    plt.colorbar()

    # Investigate why there's a difference in Cost
    # when you use a slightly different defocus than
    # the one Zemax says we used

    j = []
    focus = np.linspace(0.1, 0.2, 20)
示例#15
0
rho, theta = rho[aperture_mask], theta[aperture_mask]
rho_max = np.max(rho)
extends = [-rho_max, rho_max, -rho_max, rho_max]

# Compute the Zernike series
coef = randgen.normal(size=N_zern)
z = zern.ZernikeNaive(mask=aperture_mask)
phase_map = z(coef=coef,
              rho=rho,
              theta=theta,
              normalize_noll=False,
              mode='Jacobi',
              print_option=None)

phase_map = zern.rescale_phase_map(phase_map, peak=1)
phase_2d = zern.invert_mask(phase_map, aperture_mask)

plt.figure()
plt.imshow(phase_2d, extent=extends, cmap='jet')
plt.title("Zernike Series (%d polynomials)" % N_zern)
plt.xlabel('x')
plt.ylabel('y')
plt.colorbar()

# Compute the Power Spectral Density of the Zernike map
phase_f = fftshift(fft2(phase_2d))
power_f = (np.abs(phase_f) / N / N)**2
spatial_frequencies = fftfreq(N, d=rho_spacing)
freq_plus = spatial_frequencies[:N // 2]

# Slice the 2D result for all 4 sub-axis: +X, +Y, -X, -Y