Large update for finishing thesis

This commit is contained in:
yosata committed 2022-08-02 14:26:17 +02:00
1 parent 8e0575e0e9
commit 9ce4b7045f
25 files changed
+2316 -521

No files matched your search

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -244,7 +244,7 @@ class ParaxialMICADO:
if print_progress: print('Propagating from ELT-IFP to MCD-COL') if print_progress: print('Propagating from ELT-IFP to MCD-COL')
Ein = Eout Ein = Eout
if self.ELT_FP.wavefrontError: if self.ELT_FP.wavefrontError:
Ein *= self.ELT_FP.phase_delay(x, y, l, ip_type='surface') Ein *= self.ELT_FP.phase_delay(x, y, l, ip_type='phase')
Eout, x, y = fou.fresnel_propagation(Ein, x, y, dz=self.ELT_COL.f, oversample=None, Eout, x, y = fou.fresnel_propagation(Ein, x, y, dz=self.ELT_COL.f, oversample=None,
show=show[s], save=saveList[s], outputCoordinates=output_coordinates[s], show=show[s], save=saveList[s], outputCoordinates=output_coordinates[s],
apply_interpolation=apply_ip[s], tx=tilt_x[s], ty=tilt_y[s]) apply_interpolation=apply_ip[s], tx=tilt_x[s], ty=tilt_y[s])
+399 -212
View File
@@ -1,7 +1,10 @@
'''
Summary
'''
import numpy as np import numpy as np
import pickle import pickle
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from numba import njit
''' '''
Miscellaneous functions that do not belong specifically to any of the Miscellaneous functions that do not belong specifically to any of the
other files. other files.
@@ -13,15 +16,31 @@ import matplotlib.pyplot as plt
def perform_zero_padding(A, xs, ys): def perform_zero_padding(A, xs, ys):
''' '''
Add zero padding around the array Add zero padding around the array
Input: Input
A - 2D-Input array -----
xs,ys - The new shape after padding. A - 2D-Input array
xs,ys - The new shape after padding.
Output:
- Array with the new padded zeros Output
------
- Array with the new padded zeros
Parameters
----------
A : TYPE
Description
xs : TYPE
Description
ys : TYPE
Description
Returns
-------
TYPE
Description
''' '''
assert A.shape[0] <= xs and A.shape[1] <= ys, 'New size is smaller than array size.' assert A.shape[0] <= xs and A.shape[1] <= ys, 'New size is smaller than array size.'
x_margin = int((xs - A.shape[0])/2) x_margin = int((xs - A.shape[0])/2)
@@ -31,9 +50,21 @@ def perform_zero_padding(A, xs, ys):
def find_nearest(array, value): def find_nearest(array, value):
''' '''
For a value it finds the nearest element in an array For a value it finds the nearest element in an array
Returns the index of that element in the original Returns the index of that element in the original
array. array.
Parameters
----------
array : TYPE
Description
value : TYPE
Description
Returns
-------
TYPE
Description
''' '''
array = np.asarray(array) array = np.asarray(array)
diff = (np.abs(array - value)).argmin() diff = (np.abs(array - value)).argmin()
@@ -43,14 +74,34 @@ def find_nearest(array, value):
def find_maximum(array): def find_maximum(array):
''' '''
Finds the index of the maximum of the given array Finds the index of the maximum of the given array
Parameters
----------
array : TYPE
Description
Returns
-------
TYPE
Description
''' '''
idx = np.unravel_index(array.argmax(), array.shape) idx = np.unravel_index(array.argmax(), array.shape)
return idx return idx
def find_pixel_scale(x): def find_pixel_scale(x):
''' '''
Calculates the pixel size from the supplied array. Calculates the pixel size from the supplied array.
Parameters
----------
x : TYPE
Description
Returns
-------
TYPE
Description
''' '''
_dpix = np.mean(np.diff(x.real)) _dpix = np.mean(np.diff(x.real))
if _dpix < 1e-99: if _dpix < 1e-99:
@@ -60,7 +111,21 @@ def find_pixel_scale(x):
def select_subsection(x, y , outputCoordinates): def select_subsection(x, y , outputCoordinates):
''' '''
Returns the indices of a subsection of an x,y meshgrid Returns the indices of a subsection of an x,y meshgrid
Parameters
----------
x : TYPE
Description
y : TYPE
Description
outputCoordinates : TYPE
Description
Returns
-------
TYPE
Description
''' '''
select_1 = find_nearest(x, outputCoordinates[0])[1] select_1 = find_nearest(x, outputCoordinates[0])[1]
select_2 = find_nearest(x, outputCoordinates[1])[1] select_2 = find_nearest(x, outputCoordinates[1])[1]
@@ -71,10 +136,26 @@ def select_subsection(x, y , outputCoordinates):
def perform_extrapolation(x, y, n, m): def perform_extrapolation(x, y, n, m):
''' '''
Performs linear extrapolation of a meshgrid to a new Performs linear extrapolation of a meshgrid to a new
shape n,m shape n,m
Returns the new extrapolated x,y meshgrid Returns the new extrapolated x,y meshgrid
Parameters
----------
x : TYPE
Description
y : TYPE
Description
n : TYPE
Description
m : TYPE
Description
Returns
-------
TYPE
Description
''' '''
# Determine properties of x,y meshgrid # Determine properties of x,y meshgrid
@@ -92,36 +173,52 @@ def perform_extrapolation(x, y, n, m):
def plot_results(E, x, y, logscale=False, fig_size=(8,3), grid=False, show=True, def plot_results(E, x, y, logscale=False, fig_size=(8,3), grid=False, show=True,
colormap='hot'): colormap='hot'):
''' '''
Quick function to plot complex valued image data, for example the Quick function to plot complex valued image data, for example the
amplitude field of a PSF. The absolute value of the complex values amplitude field of a PSF. The absolute value of the complex values
will be used. will be used.
If the amplitude values can be smaller than zero, use If the amplitude values can be smaller than zero, use
plot_real_valued_results(). plot_real_valued_results().
Parameters Parameters
---------- ----------
E - 2D Array E : TYPE
Amplitude or intensity of the field to be shown. Description
x - Array x : TYPE
X-coordinates of the field to be plotted Description
y - Array y : TYPE
Y-coordinates of the field to be plotted Description
logscale - Bool (default=False) logscale : bool, optional
If True, imshow() will use a logarithmic color scale Description
fig_size - Tuple (default=(8,8)) fig_size : tuple, optional
Size of the figure Description
grid - Bool (default=False) grid : bool, optional
If True, the figure will show gridlines. Description
show - Bool (default=True) show : bool, optional
If True, the figure will be shown before continuing. Description
colormap - String (default='hot') colormap : str, optional
String, defines the colormap imshow() will use. Description
labeltext - String (default=None) E - 2D Array
Defines the label to be shown along the colorbar Amplitude or intensity of the field to be shown.
x - Array
Returns X-coordinates of the field to be plotted
------- y - Array
_fig, _ax - Matplotlib figure and axis object. Y-coordinates of the field to be plotted
logscale - Bool (default=False)
If True, imshow() will use a logarithmic color scale
fig_size - Tuple (default=(8,8))
Size of the figure
grid - Bool (default=False)
If True, the figure will show gridlines.
show - Bool (default=True)
If True, the figure will be shown before continuing.
colormap - String (default='hot')
String, defines the colormap imshow() will use.
labeltext - String (default=None)
Defines the label to be shown along the colorbar
Returns
-------
_fig, _ax - Matplotlib figure and axis object.
''' '''
x = x.real x = x.real
y = y.real y = y.real
@@ -155,33 +252,51 @@ def plot_results(E, x, y, logscale=False, fig_size=(8,3), grid=False, show=True,
def plot_real_valued_results(E, x, y, logscale=False, fig_size=(8,8), grid=False, show=True, def plot_real_valued_results(E, x, y, logscale=False, fig_size=(8,8), grid=False, show=True,
colormap='hot', labeltext=None): colormap='hot', labeltext=None):
''' '''
Quick function to plot real valued image data, for example the Quick function to plot real valued image data, for example the
intensity field of a PSF. intensity field of a PSF.
Parameters Parameters
---------- ----------
E - 2D Array E : TYPE
Amplitude or intensity of the field to be shown. Description
x - Array x : TYPE
X-coordinates of the field to be plotted Description
y - Array y : TYPE
Y-coordinates of the field to be plotted Description
logscale - Bool (default=False) logscale : bool, optional
If True, imshow() will use a logarithmic color scale Description
fig_size - Tuple (default=(8,8)) fig_size : tuple, optional
Size of the figure Description
grid - Bool (default=False) grid : bool, optional
If True, the figure will show gridlines. Description
show - Bool (default=True) show : bool, optional
If True, the figure will be shown before continuing. Description
colormap - String (default='hot') colormap : str, optional
String, defines the colormap imshow() will use. Description
labeltext - String (default=None) labeltext : None, optional
Defines the label to be shown along the colorbar Description
E - 2D Array
Returns Amplitude or intensity of the field to be shown.
------- x - Array
_fig, _ax - Matplotlib figure and axis object. X-coordinates of the field to be plotted
y - Array
Y-coordinates of the field to be plotted
logscale - Bool (default=False)
If True, imshow() will use a logarithmic color scale
fig_size - Tuple (default=(8,8))
Size of the figure
grid - Bool (default=False)
If True, the figure will show gridlines.
show - Bool (default=True)
If True, the figure will be shown before continuing.
colormap - String (default='hot')
String, defines the colormap imshow() will use.
labeltext - String (default=None)
Defines the label to be shown along the colorbar
Returns
-------
_fig, _ax - Matplotlib figure and axis object.
''' '''
x = x.real x = x.real
@@ -208,8 +323,15 @@ def plot_real_valued_results(E, x, y, logscale=False, fig_size=(8,8), grid=False
def save_variable(filename, variable): def save_variable(filename, variable):
''' '''
Dumps the given variable to a binary pickle file Dumps the given variable to a binary pickle file
for later use. for later use.
Parameters
----------
filename : TYPE
Description
variable : TYPE
Description
''' '''
with open(filename, 'wb') as f: with open(filename, 'wb') as f:
pickle.dump(variable, f, protocol=-1 ) pickle.dump(variable, f, protocol=-1 )
@@ -217,7 +339,17 @@ def save_variable(filename, variable):
def load_variable(filename): def load_variable(filename):
''' '''
Loads a binary pickle file for use. Loads a binary pickle file for use.
Parameters
----------
filename : TYPE
Description
Returns
-------
TYPE
Description
''' '''
with open(filename, 'rb') as f: with open(filename, 'rb') as f:
variable = pickle.load(f) variable = pickle.load(f)
@@ -226,26 +358,26 @@ def load_variable(filename):
def geometric_distortions(xp, yp, x, y, magnification=1): def geometric_distortions(xp, yp, x, y, magnification=1):
''' '''
Plots a grid showing the geometric distortions. Effects can be magnified. Plots a grid showing the geometric distortions. Effects can be magnified.
Parameters Parameters
---------- ----------
xp : array xp : array
Meshgrid array denoting the perturbed x-coordinates Meshgrid array denoting the perturbed x-coordinates
yp : array yp : array
Meshgrid array denoting the perturbed y-coordinates Meshgrid array denoting the perturbed y-coordinates
x : array x : array
Meshgrid array denoting the unperturbed x-coordinates Meshgrid array denoting the unperturbed x-coordinates
y : array y : array
Meshgrid array denoting the unperturbed y-coordinates Meshgrid array denoting the unperturbed y-coordinates
magnification : float (optional) magnification : float (optional)
If given, amplifies the perturbation, so that it is more visible. If given, amplifies the perturbation, so that it is more visible.
Returns Returns
------- -------
fig, ax : matplotlib.pyplot object fig, ax : matplotlib.pyplot object
Figure that can be shown using plt.show(), or modified by the user. Figure that can be shown using plt.show(), or modified by the user.
''' '''
dx = (xp - x) * magnification dx = (xp - x) * magnification
dy = (yp - y) * magnification dy = (yp - y) * magnification
@@ -267,53 +399,53 @@ def geometric_distortions(xp, yp, x, y, magnification=1):
def get_prism_rtm(x_in, y_in, theta_x_in, theta_y_in, thickness, a1x, a2x, a1y, a2y, n_glass): def get_prism_rtm(x_in, y_in, theta_x_in, theta_y_in, thickness, a1x, a2x, a1y, a2y, n_glass):
''' '''
Finds the ray transfer matrix for a prism with the given parameters. Finds the ray transfer matrix for a prism with the given parameters.
It does not assume small angles. It does not take into account any It does not assume small angles. It does not take into account any
(internal) reflections. The results were confirmed using CODEV. (internal) reflections. The results were confirmed using CODEV.
It is assumed that the prism is located inside a vacuum (i.e. n=1 It is assumed that the prism is located inside a vacuum (i.e. n=1
outside the prism). outside the prism).
Parameters Parameters
---------- ----------
x_in : float x_in : float
Input x-coordinate at the point in the plane where the optical Input x-coordinate at the point in the plane where the optical
axis reaches the front of the prism axis reaches the front of the prism
y_in : float y_in : float
Input y-coordinate at the point in the plane where the optical Input y-coordinate at the point in the plane where the optical
axis reaches the front of the prism axis reaches the front of the prism
theta_x_in : float theta_x_in : float
Input angle about the y-axis (so it affects the x-coordinates) Input angle about the y-axis (so it affects the x-coordinates)
theta_y_in : float theta_y_in : float
Input angle about the x-axis (so it affects the y-coordinates) Input angle about the x-axis (so it affects the y-coordinates)
thickness : float thickness : float
Thickness of the prism along the optical axis Thickness of the prism along the optical axis
a1x : float a1x : float
Rotation angle of the first prism surface about the y-axis, Rotation angle of the first prism surface about the y-axis,
so that the x-coordinates are affected. so that the x-coordinates are affected.
a2x : float a2x : float
Rotation angle of the second prism surface about the y-axis, Rotation angle of the second prism surface about the y-axis,
so that the x-coordinates are affected. so that the x-coordinates are affected.
a1y : float a1y : float
Rotation angle of the first prism surface about the x-axis, Rotation angle of the first prism surface about the x-axis,
so that the y-coordinates are affected. so that the y-coordinates are affected.
a2y : float a2y : float
Rotation angle of the second prism surface about the x-axis, Rotation angle of the second prism surface about the x-axis,
so that the y-coordinates are affected. so that the y-coordinates are affected.
n_glass : float n_glass : float
Refractive index of the prism glass Refractive index of the prism glass
Returns Returns
------- -------
x_out : float x_out : float
Output x-coordinate at the point in the plane where the optical Output x-coordinate at the point in the plane where the optical
axis reaches the back of the prism axis reaches the back of the prism
y_out : float y_out : float
Output y-coordinate at the point in the plane where the optical Output y-coordinate at the point in the plane where the optical
axis reaches the back of the prism axis reaches the back of the prism
theta_x_out : float theta_x_out : float
Output angle about the y-axis (so it affects the x-coordinates) Output angle about the y-axis (so it affects the x-coordinates)
theta_y_out : float theta_y_out : float
Output angle about the x-axis (so it affects the y-coordinates) Output angle about the x-axis (so it affects the y-coordinates)
''' '''
theta_x_out = -1 * np.arcsin( -n_glass * np.sin( np.arcsin( 1/n_glass * np.sin(theta_x_in - a1x) ) + a1x - a2x ) ) + a2x theta_x_out = -1 * np.arcsin( -n_glass * np.sin( np.arcsin( 1/n_glass * np.sin(theta_x_in - a1x) ) + a1x - a2x ) ) + a2x
theta_y_out = -1 * np.arcsin( -n_glass * np.sin( np.arcsin( 1/n_glass * np.sin(theta_y_in - a1y) ) + a1y - a2y ) ) + a2y theta_y_out = -1 * np.arcsin( -n_glass * np.sin( np.arcsin( 1/n_glass * np.sin(theta_y_in - a1y) ) + a1y - a2y ) ) + a2y
@@ -335,40 +467,40 @@ def get_prism_rtm(x_in, y_in, theta_x_in, theta_y_in, thickness, a1x, a2x, a1y,
def get_paraxial_lens_rtm(x_in, y_in, theta_x_in, theta_y_in, f_lens_x, f_lens_y): def get_paraxial_lens_rtm(x_in, y_in, theta_x_in, theta_y_in, f_lens_x, f_lens_y):
''' '''
Finds the ray transfer matrix of a paraxial lens, without small angle Finds the ray transfer matrix of a paraxial lens, without small angle
approximations. approximations.
Parameters Parameters
---------- ----------
x_in : float x_in : float
Input x-coordinate at the point in the plane where the optical Input x-coordinate at the point in the plane where the optical
axis reaches the front of the prism axis reaches the front of the prism
y_in : float y_in : float
Input y-coordinate at the point in the plane where the optical Input y-coordinate at the point in the plane where the optical
axis reaches the front of the prism axis reaches the front of the prism
theta_x_in : float theta_x_in : float
Input angle about the y-axis (so it affects the x-coordinates) Input angle about the y-axis (so it affects the x-coordinates)
theta_y_in : float theta_y_in : float
Input angle about the x-axis (so it affects the y-coordinates) Input angle about the x-axis (so it affects the y-coordinates)
f_lens_x : float f_lens_x : float
Focal length of the lens for the points with a nonzero x-coordinate Focal length of the lens for the points with a nonzero x-coordinate
value. value.
f_lens_y : float f_lens_y : float
Focal length of the lens for the points with a nonzero y-coordinate Focal length of the lens for the points with a nonzero y-coordinate
value. value.
Returns Returns
------- -------
x_out : float x_out : float
Output x-coordinate at the point in the plane where the optical Output x-coordinate at the point in the plane where the optical
axis reaches the back of the prism axis reaches the back of the prism
y_out : float y_out : float
Output y-coordinate at the point in the plane where the optical Output y-coordinate at the point in the plane where the optical
axis reaches the back of the prism axis reaches the back of the prism
theta_x_out : float theta_x_out : float
Output angle about the y-axis (so it affects the x-coordinates) Output angle about the y-axis (so it affects the x-coordinates)
theta_y_out : float theta_y_out : float
Output angle about the x-axis (so it affects the y-coordinates) Output angle about the x-axis (so it affects the y-coordinates)
''' '''
x_out = x_in x_out = x_in
y_out = y_in y_out = y_in
@@ -379,39 +511,94 @@ def get_paraxial_lens_rtm(x_in, y_in, theta_x_in, theta_y_in, f_lens_x, f_lens_y
def get_free_space_propagation_rtm(x_in, y_in, theta_x_in, theta_y_in, distance): def get_free_space_propagation_rtm(x_in, y_in, theta_x_in, theta_y_in, distance):
''' '''
Finds the ray transfer matrix of a free space propagation, without small Finds the ray transfer matrix of a free space propagation, without small
angle approximations. angle approximations.
Parameters Parameters
---------- ----------
x_in : float x_in : float
Input x-coordinate at the point in the plane where the optical Input x-coordinate at the point in the plane where the optical
axis reaches the front of the prism axis reaches the front of the prism
y_in : float y_in : float
Input y-coordinate at the point in the plane where the optical Input y-coordinate at the point in the plane where the optical
axis reaches the front of the prism axis reaches the front of the prism
theta_x_in : float theta_x_in : float
Input angle about the y-axis (so it affects the x-coordinates) Input angle about the y-axis (so it affects the x-coordinates)
theta_y_in : float theta_y_in : float
Input angle about the x-axis (so it affects the y-coordinates) Input angle about the x-axis (so it affects the y-coordinates)
distance : float distance : float
Distance to travel through free space. Distance to travel through free space.
Returns Returns
------- -------
x_out : float x_out : float
Output x-coordinate at the point in the plane where the optical Output x-coordinate at the point in the plane where the optical
axis reaches the back of the prism axis reaches the back of the prism
y_out : float y_out : float
Output y-coordinate at the point in the plane where the optical Output y-coordinate at the point in the plane where the optical
axis reaches the back of the prism axis reaches the back of the prism
theta_x_out : float theta_x_out : float
Output angle about the y-axis (so it affects the x-coordinates) Output angle about the y-axis (so it affects the x-coordinates)
theta_y_out : float theta_y_out : float
Output angle about the x-axis (so it affects the y-coordinates) Output angle about the x-axis (so it affects the y-coordinates)
''' '''
x_out = x_in + distance * np.tan(theta_x_in) x_out = x_in + distance * np.tan(theta_x_in)
y_out = y_in + distance * np.tan(theta_y_in) y_out = y_in + distance * np.tan(theta_y_in)
theta_x_out = theta_x_in theta_x_out = theta_x_in
theta_y_out = theta_y_in theta_y_out = theta_y_in
return x_out, y_out, theta_x_out, theta_y_out return x_out, y_out, theta_x_out, theta_y_out
def fourier_series(x, y, c, Px=30, Py=30, xs=10, ys=10):
'''
Calculates the 2D continous Fourier Series for a set of
coefficients. This allows for interpolation on a random
phase field without artefacts (in theory...).
It is however, really quite slow for large arrays.
Parameters
----------
x : meshgrid array
x-coordinates of the 2D array of interest
y : meshgrid array
y-coordinates of the 2D array of interest
c : 2D array
coefficients of the Fourier Series
Px : int, optional
Period length in x
Py : int, optional
Period length in y
xs : int, optional
Shift of the symmetry point x
ys : int, optional
Shift of the symmetry point in y
Returns
-------
array
2D array of the Fourier Series
'''
# Shift the coordinates to remove the symmetry point
# from the output field.
xpos = x + np.sqrt(2)*xs
ypos = y + np.sqrt(2)*ys
normf = np.sum(c)
# Preparation for the calculation, to prevent doing
# the same thing over and over again.
xt = 2*np.pi*xpos/Px
yt = 2*np.pi*ypos/Py
# Doing the calculation using numba
fout = numba_fourier_series(c, xt, yt)
return fout/normf
@njit
def numba_fourier_series(c, xt, yt):
cs0, cs1 = c.shape
fout = np.zeros(xt.shape, dtype=np.float64)
for j in range(cs1):
for i in range(cs0):
v = c[i,j] * np.exp(1j * (i * xt + j * yt))
fout += v.real
return fout
+2 -3
View File
@@ -475,16 +475,15 @@ class OpticalSurface:
wfeMap = interp2d(ximg[0,:], yimg[:,0], self.sag, kind=kind, **kwargs) wfeMap = interp2d(ximg[0,:], yimg[:,0], self.sag, kind=kind, **kwargs)
wavefrontErrorMap = wfeMap(x[0,:].real, y[:,0].real) wavefrontErrorMap = wfeMap(x[0,:].real, y[:,0].real)
_t_l *= np.exp(1j * _k * wavefrontErrorMap ) _t_l *= np.exp(1j * _k * wavefrontErrorMap )
elif ip_type == 'phase': elif ip_type in ['phase', 'fourier_series']:
try: try:
xy = deepcopy((x,y)) xy = deepcopy((x,y))
xy_img = deepcopy(self.sag_coordinates) xy_img = deepcopy(self.sag_coordinates)
wavefrontErrorMap = self.wfe.generate_height_map_window( wavefrontErrorMap = self.wfe.generate_height_map_window(
xy, xy_img, ip_type='phase', kind=kind) xy, xy_img, ip_type=ip_type, kind=kind)
_t_l *= np.exp(1j * _k * wavefrontErrorMap ) _t_l *= np.exp(1j * _k * wavefrontErrorMap )
except AttributeError: except AttributeError:
print('No WavefrontError object detected. Continuing without!') print('No WavefrontError object detected. Continuing without!')
if self.mask is not None: if self.mask is not None:
_t_l *= self.mask _t_l *= self.mask
return _t_l return _t_l
+49 -7
View File
@@ -4,6 +4,7 @@ import types
from math import ceil from math import ceil
from scipy.special import factorial, j1 from scipy.special import factorial, j1
from scipy import interpolate from scipy import interpolate
from .misc import fourier_series
class WavefrontError: class WavefrontError:
''' '''
@@ -277,7 +278,7 @@ class WavefrontError:
return _phaseAngle return _phaseAngle
def generate_height_map(self, s0, p, rho_c, addToData=False): def generate_height_map(self, s0, p, rho_c, addToData=False,):
''' '''
This function generates a surface height distribution based This function generates a surface height distribution based
on a Lorentzian PSD profile. Following the paper by E. Sidick (2009). on a Lorentzian PSD profile. Following the paper by E. Sidick (2009).
@@ -289,9 +290,14 @@ class WavefrontError:
_dx = 1 / self.m _dx = 1 / self.m
_dy = 1 / self.n _dy = 1 / self.n
_A = (self.m * _dx) * (self.n * _dy) _A = (self.m * _dx) * (self.n * _dy)
_phase = np.random.uniform(0,1,(self.n,self.m)) try:
self.phase = _phase _phase = self.phase
print('Phase Attribute present!')
except AttributeError:
_phase = np.random.uniform(0,1,(self.n,self.m))
self.phase = _phase
# Calculating the PSD and phase # Calculating the PSD and phase
_PSD, _rho_mn_list = self.power_spectral_density(s0, p, rho_c, _dx, _dy, _A) _PSD, _rho_mn_list = self.power_spectral_density(s0, p, rho_c, _dx, _dy, _A)
_phiMap = self.phase_map(_phase) _phiMap = self.phase_map(_phase)
@@ -327,12 +333,15 @@ class WavefrontError:
xy - tuple containing the x and y coordinates of the small window xy - tuple containing the x and y coordinates of the small window
xy_img - tuple containing the x and y coordinates of the full plane xy_img - tuple containing the x and y coordinates of the full plane
to which the wfe is applied. to which the wfe is applied.
ip_type - 'phase' or 'surface' ip_type - 'phase' or 'surface' or 'fourier_series'
Defines what method to use to obtain the interpolated surface Defines what method to use to obtain the interpolated surface
height map. 'surface' does a normal interpolation on the height height map. 'surface' does a normal interpolation on the height
map. This does not conserve the WFE rms value. Then, 'phase' map. This does not conserve the WFE rms value. Then, 'phase'
interpolates on the phase information in the PSD algorithm. This interpolates on the phase information in the PSD algorithm. This
does conserve the WFE rms within the window. does conserve the WFE rms within the window.
The last option, 'fourier_series' is very slow, but does provide
an analytical and continous solution that does not rely on
interpolation.
''' '''
# normalise the coordinates to -1 to 1 # normalise the coordinates to -1 to 1
ximg, yimg = xy_img ximg, yimg = xy_img
@@ -376,14 +385,47 @@ class WavefrontError:
ptvw = np.abs(np.max(_hMapw.real)) + np.abs(np.min(_hMapw.real)) ptvw = np.abs(np.max(_hMapw.real)) + np.abs(np.min(_hMapw.real))
elif ip_type == 'surface': elif ip_type == 'surface':
print('Hello there!')
assert hasattr(self, 'data'), 'No wfe data available to interpolate on!' assert hasattr(self, 'data'), 'No wfe data available to interpolate on!'
try: try:
_hMapw = self.wfe_ip(x[0,:], y[:,0]) _hMapw = self.wfe_ip(x[0,:], y[:,0])
except AttributeError: except AttributeError:
print('Hello there 2!')
self.wfe_ip = interpolate.interp2d(ximg[0,:], yimg[:,0], self.data, kind=kind) self.wfe_ip = interpolate.interp2d(ximg[0,:], yimg[:,0], self.data, kind=kind)
_hMapw = self.wfe_ip(x[0,:], y[:,0]) _hMapw = self.wfe_ip(x[0,:], y[:,0])
elif ip_type == 'fourier_series':
# Check if the coefficients are present
assert hasattr(self, 'coefficients'), 'No coefficients for the Fourier Series present!'
# Reinterpreting the coordinates, because they do not need to be scaled here
x, y = xy
x, y = x.real, y.real
# Calculation of the necessary constants
_dx = 1 / self.m
_dy = 1 / self.n
_A = (self.m * _dx) * (self.n * _dy)
# Generating the array at coordinates x, y
print('Applying the Fourier series. This might take a while...')
_phase_window = fourier_series(x, y, self.coefficients)
print('Fourier series calculated!')
# Generating the surface
_PSDw, _rho_mn_listw = self.PSD2D, self.rho_mn_list
_phiMap_window = self.phase_map(_phase_window)
_PSDw = np.fft.ifftshift(_PSDw)
# Doing the fourier transform of the window
_inw = pyfftw.empty_aligned((self.n,self.m), dtype='complex128')
_inw[:] = np.exp( 1j * _phiMap_window) * np.sqrt(_A * np.abs(_PSDw))
_outw = pyfftw.empty_aligned((self.n,self.m), dtype='complex128')
_FFTW_Objw = pyfftw.FFTW( _inw, _outw, axes=(0,1), direction='FFTW_BACKWARD', threads=6, flags=('FFTW_ESTIMATE',))
_FFTW_Objw()
_hMapw = 1 / ( _dx * _dy ) * _outw
rmsw = np.sqrt(np.mean(np.asarray(_hMapw.real)**2))
ptvw = np.abs(np.max(_hMapw.real)) + np.abs(np.min(_hMapw.real))
if not addToData: if not addToData:
self.data_window = _hMapw.real self.data_window = _hMapw.real
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,86 @@
import numpy as np
import matplotlib.pyplot as plt
import sys
# sys.path.insert(0, '/Users/joost/Dropbox/Werk/PhD/Analyses/Python/Analytical/Fourier Optics')
# sys.path.insert(0, '/home/joost/Dropbox/Werk/PhD/Analyses/Python/Analytical/Fourier Optics')
sys.path.insert(0, '/Users/joost/Dropbox/Werk/PhD/Documents/Published/FourierOptics GitHub release/')
from fourierOptics import *
from pdb import set_trace
'''
@Author: Joost van den Born
@Date: 14 July, 2022
@Description: I use this script to create a large array (1001x1001 and 3001x3001)
that can be used for the phase screen in the WFE production.
Because it takes very long to generate these at this high resolution,
we will need to save them to a pickle file.
What is saved:
The array of coefficients
The array of the full phase field
To Do:
- Normalize/Scale the amplitude in an appropriate manner.
'''
if __name__ == '__main__':
# plt.rc('text', usetex=True)
# plt.rc('font', family='serif')
import time
# Setup of simulation
l = 1.49e-3
# mcd = ParaxialMICADO(n=1001, m=1001, dpix=160)
mcd = ParaxialMICADO(n=3001, m=3001, dpix=50)
# mcd.plot_ray_trace()
wfe = WavefrontError(mcd.n, mcd.m)
# -------------------------------------------------------
####### GENERATING THE PHASE WITH A FOURIER SERIES ######
# -------------------------------------------------------
# Get x3 and y3 (the coordinates to apply the ADC WFE to):
# The focal plane area is about 200x200 mm, so we apply the wavefront error to an area
# of 200x200 mm.
xfp, yfp = np.meshgrid(np.linspace(-150, 150, mcd.n), np.linspace(-150, 150, mcd.m))
# np.random.seed(2183)
# coeff = np.random.uniform(-1,1,(int(mcd.n/3),int(mcd.m/3)))
np.random.seed(2183)
coeff = np.random.uniform(-1,1,(101,101))
if False:
print('Going in...')
s1 = time.time()
fs_array = fourier_series(xfp, yfp, coeff, Px=300, Py=300, xs=150, ys=150)
s2 = time.time()
print('Finished!')
print('That took me {:.4f} s!'.format(s2-s1) )
save_variable('wfe_fp_phase_array_coefficients_{}_{}.pkl'.format(mcd.n, mcd.m), coeff)
save_variable('wfe_fp_phase_array_{}_{}.pkl'.format(mcd.n, mcd.m), fs_array)
# -------------------------------------------------------
# -------------------------------------------------------
# -------------------------------------------------------
if True:
# coeff = load_variable('wfe_fp_phase_array_coefficients_{}_{}.pkl'.format(mcd.n, mcd.m))
fs_array = load_variable('wfe_fp_phase_array_{}_{}.pkl'.format(mcd.n, mcd.m))
wfe.phase = fs_array
surf = wfe.generate_height_map(50e-6, 2, 1)
rms = np.sqrt(np.mean(surf**2))
print('RMS: {}'.format(rms))
plt.figure()
plt.imshow(fs_array, cmap='jet', extent=[xfp.min(), xfp.max(), yfp.min(), yfp.max()], origin='lower')
plt.colorbar()
plt.show()
plt.figure()
plt.imshow(surf, cmap='jet', extent=[xfp.min(), xfp.max(), yfp.min(), yfp.max()], origin='lower')
plt.colorbar()
plt.show()
@@ -16,14 +16,14 @@ from pdb import set_trace
Script made for the SPIE manuscript. Script made for the SPIE manuscript.
''' '''
def plot_distortions(): def plot_distortions(xe, ye, xf, yf):
# Coordinates without WFEs # Coordinates without WFEs
xe = load_variable('../data/GD_IFP_obscured_PSF_xcentroid_expected_zern.pkl') # xe = load_variable('../data/GD_IFP_zoom93_obscured_PSF_xcentroid_expected_zern_psd.pkl')
ye = load_variable('../data/GD_IFP_obscured_PSF_ycentroid_expected_zern.pkl') # ye = load_variable('../data/GD_IFP_zoom93_obscured_PSF_ycentroid_expected_zern_psd.pkl')
# Coordinates with WFEs # # Coordinates with WFEs
xf = load_variable('../data/GD_IFP_obscured_PSF_xcentroid_found_with_wfe_airy_zern.pkl') # xf = load_variable('../data/GD_IFP_zoom93_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yf = load_variable('../data/GD_IFP_obscured_PSF_ycentroid_found_with_wfe_airy_zern.pkl') # yf = load_variable('../data/GD_IFP_zoom93_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
mcd = ParaxialMICADO(n=3001, m=3001, dpix=50, l=1.49e-3) mcd = ParaxialMICADO(n=3001, m=3001, dpix=50, l=1.49e-3)
@@ -53,8 +53,8 @@ def plot_distortions():
ax.set_xlabel('x (mm)') ax.set_xlabel('x (mm)')
ax.set_ylabel('y (mm)') ax.set_ylabel('y (mm)')
fig.colorbar(im, ax=ax, label='PSF centroid shift ($\\mu$as)') fig.colorbar(im, ax=ax, label='PSF centroid shift ($\\mu$as)')
plt.savefig('../figures/UPDATED_GD_focal_plane_zern.pdf', bbox_inches='tight', transparent=True) # plt.savefig('../figures/UPDATED_GD_zoom93_focal_plane_zern.pdf', bbox_inches='tight', transparent=True)
plt.savefig('../figures/UPDATED_GD_focal_plane_zern.png', bbox_inches='tight', transparent=True) # plt.savefig('../figures/UPDATED_GD_zoom93_focal_plane_zern.png', bbox_inches='tight', transparent=True)
plt.tight_layout() plt.tight_layout()
plt.show() plt.show()
@@ -62,11 +62,10 @@ def plot_distortions():
if __name__ == '__main__': if __name__ == '__main__':
# plt.rc('text', usetex=True) # plt.rc('text', usetex=True)
# plt.rc('font', family='serif') # plt.rc('font', family='serif')
# Setup of simulation # Setup of simulation
l = 1.49e-3 l = 1.49e-3
mcd = ParaxialMICADO(n=3001, m=3001, dpix=50) mcd = ParaxialMICADO(n=3001, m=3001, dpix=50)
# mcd.plot_ray_trace() print(mcd.platescale)
wfe = WavefrontError(mcd.n, mcd.m) wfe = WavefrontError(mcd.n, mcd.m)
# Get x3 and y3 (the coordinates to apply the ADC WFE to): # Get x3 and y3 (the coordinates to apply the ADC WFE to):
@@ -80,34 +79,91 @@ if __name__ == '__main__':
print('\nSetting the wavefront errors...') print('\nSetting the wavefront errors...')
wfe_fp = WavefrontError(mcd.n,mcd.m) wfe_fp = WavefrontError(mcd.n,mcd.m)
wfe_rms = 50e-6 wfe_rms = 50e-6
#PSD Wavefront error
# surface_map_fp = wfe_fp.generate_height_map(wfe_rms, 2, 1)
# plot_real_valued_results(surface_map_fp*1e6, xfp, yfp, logscale=False, fig_size=(8,6.4), grid=False, show=True,
# colormap='jet', labeltext='Surface height (nm)')
# save_variable('../data/MCD_IFP_wfe_object_PSD_50nm.pkl', wfe_fp)
# wfe_fp = load_variable('../data/MCD_IFP_wfe_object_PSD_50nm.pkl')
# surface_map_fp = wfe_fp.data
# Zernike wavefront error psdWFE = True
# We re0se the ADC_surface classes because it can be applicable to any surface, if used properly zernikeWFE = False
ifp_wfe = ADC_surfaces(xfp, yfp, clearAperture=300)
ifp_wfe.generate_ADC_wfe(zernRMS=wfe_rms, psdRMS=0.) if psdWFE:
mask = np.sqrt(ifp_wfe.xz**2 + ifp_wfe.yz**2) > 1 #PSD Wavefront error
# surface_map_fp = wfe_fp.generate_height_map(wfe_rms, 2, 1)
# Plot the wfe # plot_real_valued_results(surface_map_fp*1e6, xfp, yfp, logscale=False, fig_size=(8,6.4), grid=False, show=True,
# surf_ifp = ifp_wfe.latest_surface.copy() # colormap='jet', labeltext='Surface height (nm)')
# surf_ifp[mask]=np.nan # save_variable('../data/MCD_IFP_wfe_object_PSD_50nm.pkl', wfe_fp)
# wfe_fp = load_variable('../data/MCD_IFP_wfe_object_PSD_50nm.pkl')
# surface_map_fp = surf_ifp wfe_fp = load_variable('../data/GD Focal Plane WFEs/MCD_IFP_wfe_object_PSD_50nm.pkl')
# wfe_fp.data = surf_ifp surface_map_fp = wfe_fp.data
wfe_fp = load_variable('../data/MCD_IFP_wfe_object_zern_50nm.pkl')
surface_map_fp = wfe_fp.data # from copy import deepcopy
# save_variable('../data/MCD_IFP_wfe_object_zern_50nm.pkl', wfe_fp) # xy_img = deepcopy((xfp, yfp))
# x, y = np.meshgrid(np.linspace(-10, 10, mcd.n), np.linspace(-10, 10, mcd.m))
# xy = deepcopy((x,y))
# zoom_map = wfe_fp.generate_height_map_window(
# xy, xy_img, ip_type='phase')
# plt.figure()
# plt.grid()
# plt.plot(1e3*x[1500,:], 1e6*zoom_map[1500,:], '-')
# # plt.imshow(1e6*zoom_map, cmap='jet', extent=[x.min(), x.max(), y.min(), y.max()],
# # origin='lower', vmin=-150, vmax=150)
# plt.xlabel('x (μm)')
# plt.ylabel('surface heigth (nm)')
# plt.show()
if zernikeWFE:
# Zernike wavefront error
# We reuse the ADC_surface classes because it can be applicable to any surface, if used properly
ifp_wfe = ADC_surfaces(xfp, yfp, clearAperture=300)
ifp_wfe.generate_ADC_wfe(zernRMS=wfe_rms, psdRMS=0.)
mask = np.sqrt(ifp_wfe.xz**2 + ifp_wfe.yz**2) > 1
# Plot the wfe
# surf_ifp = ifp_wfe.latest_surface.copy()
# surf_ifp[mask]=np.nan
# surface_map_fp = surf_ifp
# wfe_fp.data = surf_ifp
# wfe_fp = load_variable('../data/GD Focal Plane WFEs/MCD_IFP_wfe_object_zern_50nm.pkl')
surface_map_fp = wfe_fp.data
# save_variable('../data/MCD_IFP_wfe_object_zern_50nm.pkl', wfe_fp)
# figp1, axp1 = plot_real_valued_results(surface_map_fp*1e6, xfp, yfp, # figp1, axp1 = plot_real_valued_results(surface_map_fp*1e6, xfp, yfp,
# colormap='jet', labeltext='Surface height (nm)', fig_size=None, show=True) # colormap='jet', labeltext='Surface height (nm)', fig_size=None, show=True)
plt.show() # plt.show()
# plt.close()
# List of performed simulations
# Defined by PSD
## GD_IFP : -1:1
## GD_IFP_zoom : -1/11 :1/11
## GD_IFP_zoom2 : -.5/11:.5/11
## GD_IFP_zoom3 : -.1/11:.1/11
## GD_IFP_zoom7 : -.02/11:.02/11, surface interpolation
## GD_IFP_zoom8 : -.05/11:.05/11, surface interpolation
## GD_IFP_zoom9 : -.01/11:.01/11, surface interpolation
## GD_IFP_zoom91 : -.01/11:.01/11 but hx and hy shifted to an area near the corner of the image, where distortion is supposed to be large
## GD_IFP_zoom92 : -.01/11:.01/11, phase interpolation
## GD_IFP_zoom93 : -.004/11:.004/11, phase interpolation
# (implemented)IDEA: Automate it, let it run all simulations for the lower left corner. Take multiples of 11 for the field points
# i.e. 1/11, 1/11**2, 1/11**3, 1/11**4
# GD_IFP_zoom94 : - 1/11:1/11, phase interpolation, with shift to left bottom corner
# GD_IFP_zoom95 : - 1/11**2:1/11**2, phase interpolation, with shift to left bottom corner
# GD_IFP_zoom96 : - 1/11**3:1/11**3, phase interpolation, with shift to left bottom corner
# GD_IFP_zoom97 : - 1/11:1/11, phase interpolation, center of the field
# GD_IFP_zoom98 : - 1/11**2:1/11**2, phase interpolation, center of the field
# GD_IFP_zoom99 : - 1/11**3:1/11**3, phase interpolation, center of the field
# GD_IFP_zoom100 : - 1/11**4:1/11**4, phase interpolation, center of the field
# GD_IFP_zoom101 : - 1/11**4:1/11**4, phase interpolation, center of the field, with shift to left bottom corner
# Defined by zernikes
## GD_IFP_obscured_PSF_xcentroid_expected_zern : 50 nm rms 19 zernike modes : -1 : 1
## To do; check PSF shape at center field with zern wfe
## To do; check interpolated WFE at any zoomed in field point.
## GD_IFP_zoom4 : -.02/11:.02/11 (actually zernike WFE!), surface interpolation --> CONTINUOUS :D
## GD_IFP_zoom5 : -.05/11:.05/11 (actually zernike WFE!), surface interpolation --> CONTINUOUS :D
## GD_IFP_zoom6 : -.05/11:.05/11, surface interpolation, but hx and hy shifted to an area near the corner of the image (actually zern!)
Elist, Eplist = [], [] Elist, Eplist = [], []
xlist, ylist = [], [] xlist, ylist = [], []
xplist, yplist = [], [] xplist, yplist = [], []
@@ -117,96 +173,99 @@ if __name__ == '__main__':
# Set field points # Set field points
hxList, hyList = np.mgrid[-1:1:11j,-1:1:11j]-(1/300) hxList, hyList = np.mgrid[-1:1:11j,-1:1:11j]-(1/300)
# hxList, hyList = np.mgrid[-.1/11:.1/11:11j,-.1/11:.1/11:11j]-(1/300) # Zoomed in to central area # hxList, hyList = np.mgrid[-.1/11:.1/11:11j,-.1/11:.1/11:11j]-(1/300) # Zoomed in to central area
# hxList, hyList = np.mgrid[-.004/11:.004/11:11j,-.004/11:.004/11:11j]-(1/300) # Zoomed in to central area
hxList = hxList.flatten() hxList = hxList.flatten()
hyList = hyList.flatten() hyList = hyList.flatten()
# List of performed simulations # Zooming in around -84mm by -84mm on the image plane
# Defined by PSD ihx = hxList[108]
## GD_IFP : -1:1 ihy = hyList[108]
## GD_IFP_zoom : -1/11 :1/11
## GD_IFP_zoom2 : -.5/11:.5/11
## GD_IFP_zoom3 : -.1/11:.1/11
# Defined by zernikes for p in [4]:
## GD_IFP_obscured_PSF_xcentroid_expected_zern : 50 nm rms 19 zernike modes : -1 : 1 x_expected_list, y_expected_list = [], []
## To do; check PSF shape at center field with zern wfe x_centroid_list, y_centroid_list = [], []
## To do; check interpolated WFE at any zoomed in field point.
# Propagate through the field positions without any WFEs hxList, hyList = np.mgrid[ihx-1/11**p:ihx+1/11**p:11j,
for i in range(len(hxList)): ihy-1/11**p:ihy+1/11**p:11j]-(1/300)
hx = hxList[i] hxList = hxList.flatten()
hy = hyList[i] hyList = hyList.flatten()
print('hx: {} - hy: {}'.format(hx,hy) )
# Initializing MICADO paraxial model # Propagate through the field positions without any WFEs
mcd = ParaxialMICADO(n=3001, m=3001, dpix=50, entrance_pupil='obscured') for i in range(len(hxList)):
print('Field point #: {}/{}'.format(i+1, len(hxList)))
hx = hxList[i]
hy = hyList[i]
print('hx: {} - hy: {}'.format(hx,hy) )
# Propagate through the MICADO system # Initializing MICADO paraxial model
E, x, y = mcd.propagate(l=l,surface=-1, zoom=False, show=False, mcd = ParaxialMICADO(n=3001, m=3001, dpix=50, entrance_pupil='obscured')
print_progress=False, hx=hx, hy=hy)
# Find approximation for the centroid and amplitude as a first # Propagate through the MICADO system
# guess for the Obscured Airy pattern fit. E, x, y = mcd.propagate(l=l,surface=-1, zoom=False, show=False,
amplitude_guess = np.max(np.abs(E))**2 print_progress=False, hx=hx, hy=hy)
xbc, ybc, _, _ = mcd.centroid_shift(method='Barycenter')
print('Barycenter guess: ', xbc, ybc)
# Do the Obscured Airy Pattern Fit for the unperturbed PSF # Find approximation for the centroid and amplitude as a first
xe, ye, _, _ = mcd.centroid_shift(method='Obscured Airy', initialGuess=(amplitude_guess, xbc, ybc, 0.0, 0.03)) # guess for the Obscured Airy pattern fit.
print('Airy result: ', xe, ye) amplitude_guess = np.max(np.abs(E))**2
x_expected_list.append(xe) xbc, ybc, _, _ = mcd.centroid_shift(method='Barycenter')
y_expected_list.append(ye) print('Barycenter guess: ', xbc, ybc)
# Do the Obscured Airy Pattern Fit for the unperturbed PSF
xe, ye, _, _ = mcd.centroid_shift(method='Obscured Airy', initialGuess=(amplitude_guess, xbc, ybc, 0.0, 0.03))
print('Airy result: ', xe, ye)
x_expected_list.append(xe)
y_expected_list.append(ye)
# Load in the WFEs and set them to the corresponding ADC surfaces
print('\nSetting the wavefront errors...')
# null_wfe = np.zeros((3001,3001))
mcd.ELT_FP.add_wavefront_error(surface_map_fp, xfp, yfp, wfe=wfe_fp)
# Propagate through the MICADO system
print('Propagating with Wavefront Errors applied...')
Ep, xp, yp = mcd.propagate(l=l,surface=-1, zoom=False, show=False,
print_progress=False, hx=hx, hy=hy)
# Do the Obscured Airy Pattern Fit for the perturbed PSF # Load in the WFEs and set them to the corresponding ADC surfaces
xf, yf, _, _ = mcd.centroid_shift(method='Obscured Airy', initialGuess=(amplitude_guess, xe, ye, 0.0, 0.03)) print('\nSetting the wavefront errors...')
# null_wfe = np.zeros((3001,3001))
# Resulting centroid shift due to the WFE mcd.ELT_FP.add_wavefront_error(surface_map_fp, xfp, yfp, wfe=wfe_fp)
x_centroid_list.append(xf)
y_centroid_list.append(yf)
R = np.sqrt( (xf-xe)**2 + (yf-ye)**2 ) * mcd.platescale * 1e6
print('xf: {}, yf: {}, shift (uas): {}'.format(xf,yf, R))
# Clear the memory by emptying the large arrays
mcd = None
wfe = None
E, x, y = None, None, None
Ep, xp, yp = None, None, None
# Reshaping the arrays # Propagate through the MICADO system
shape = (int(np.sqrt(len(x_expected_list))), int(np.sqrt(len(x_expected_list)))) print('Propagating with Wavefront Errors applied...')
x_expected = np.asarray(x_expected_list).reshape(shape) Ep, xp, yp = mcd.propagate(l=l,surface=-1, zoom=False, show=False,
y_expected = np.asarray(y_expected_list).reshape(shape)#.T print_progress=False, hx=hx, hy=hy)
# Saving the arrays # Do the Obscured Airy Pattern Fit for the perturbed PSF
save_variable('../data/GD_IFP_obscured_PSF_xcentroid_expected_zern.pkl', x_expected) xf, yf, _, _ = mcd.centroid_shift(method='Obscured Airy', initialGuess=(amplitude_guess, xe, ye, 0.0, 0.03))
save_variable('../data/GD_IFP_obscured_PSF_ycentroid_expected_zern.pkl', y_expected)
# Resulting centroid shift due to the WFE
x_centroid_list.append(xf)
y_centroid_list.append(yf)
R = np.sqrt( (xf-xe)**2 + (yf-ye)**2 ) * mcd.platescale * 1e6
print('xf: {}, yf: {}, shift (uas): {}'.format(xf,yf, R))
# Clear the memory by emptying the large arrays
mcd = None
wfe = None
E, x, y = None, None, None
Ep, xp, yp = None, None, None
# Reshaping the arrays # Reshaping the arrays
shape = (int(np.sqrt(len(x_centroid_list))), int(np.sqrt(len(x_centroid_list)))) shape = (int(np.sqrt(len(x_expected_list))), int(np.sqrt(len(x_expected_list))))
x_centroid = np.asarray(x_centroid_list).reshape(shape) x_expected = np.asarray(x_expected_list).reshape(shape)
y_centroid = np.asarray(y_centroid_list).reshape(shape) y_expected = np.asarray(y_expected_list).reshape(shape)#.T
# Saving the arrays
save_variable('../data/GD_IFP_obscured_PSF_xcentroid_found_with_wfe_airy_zern.pkl', x_centroid)
save_variable('../data/GD_IFP_obscured_PSF_ycentroid_found_with_wfe_airy_zern.pkl', y_centroid)
plot_distortions() # Saving the arrays
zoom_value = 97 + p
save_variable('../data/GD_IFP_zoom{:.0f}_obscured_PSF_xcentroid_expected_zern_psd.pkl'.format(zoom_value), x_expected)
save_variable('../data/GD_IFP_zoom{:.0f}_obscured_PSF_ycentroid_expected_zern_psd.pkl'.format(zoom_value), y_expected)
# Reshaping the arrays
shape = (int(np.sqrt(len(x_centroid_list))), int(np.sqrt(len(x_centroid_list))))
x_centroid = np.asarray(x_centroid_list).reshape(shape)
y_centroid = np.asarray(y_centroid_list).reshape(shape)
# Saving the arrays
save_variable('../data/GD_IFP_zoom{:.0f}_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl'.format(zoom_value), x_centroid)
save_variable('../data/GD_IFP_zoom{:.0f}_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl'.format(zoom_value), y_centroid)
plot_distortions(x_expected, y_expected, x_centroid, y_centroid)
@@ -0,0 +1,214 @@
import numpy as np
import sys
# sys.path.insert(0, '/Users/joost/Dropbox/Werk/PhD/Analyses/Python/Analytical/Fourier Optics')
# sys.path.insert(0, '/home/joost/Dropbox/Werk/PhD/Analyses/Python/Analytical/Fourier Optics')
sys.path.insert(0, '/Users/joost/Dropbox/Werk/PhD/Documents/Published/FourierOptics GitHub release/')
from fourierOptics import *
from pdb import set_trace
'''
Author: Joost van den Born, PhD student at University of Groningen
Contact: born@astro.rug.nl
Description: Script to generate the geometric distortions due to WFEs
off the ADC prisms. The distortions are produced by measuring
the offset of the PSF centroid with and without WFE as a
function of field position.
Script made for the SPIE manuscript.
'''
def plot_distortions():
# Coordinates without WFEs
xe = load_variable('../data/GD_IFP_obscured_PSF_xcentroid_expected_fs.pkl')
ye = load_variable('../data/GD_IFP_obscured_PSF_ycentroid_expected_fs.pkl')
# Coordinates with WFEs
xf = load_variable('../data/GD_IFP_obscured_PSF_xcentroid_found_with_wfe_airy_fs.pkl')
yf = load_variable('../data/GD_IFP_obscured_PSF_ycentroid_found_with_wfe_airy_fs.pkl')
mcd = ParaxialMICADO(n=3001, m=3001, dpix=50, l=1.49e-3)
n = xe.shape[0]
X = xe.flatten()
Y = ye.flatten()
dX = xf - xe
dY = yf - ye
dx = dX.flatten()
dy = dY.flatten()
# Magnitude of the distortions
R = np.sqrt(dx**2 + dy**2) * mcd.platescale * 1e6
# Dummy figure, used for the colorbar colors in the quiver plot
im = plt.imshow(np.reshape(R, (n,n)))
plt.close()
fig = plt.figure()
ax = fig.add_subplot()
for i in range(len(xe)):
ax.plot(xe[:,i], ye[:,i], 'k-', alpha=0.2, linewidth=.5)
ax.plot(xe[i,:], ye[i,:], 'k-', alpha=0.2, linewidth=.5)
ax.quiver(X,Y,dx,dy, angles='xy', edgecolor='k', facecolor='None', linewidth=.5, units='dots')
ax.quiver(X,Y,dx,dy,R, alpha=.5, units='dots')
ax.set_aspect('equal', adjustable='box')
ax.set_xlabel('x (mm)')
ax.set_ylabel('y (mm)')
fig.colorbar(im, ax=ax, label='PSF centroid shift ($\\mu$as)')
plt.savefig('../figures/UPDATED_GD_focal_plane_fs.pdf', bbox_inches='tight', transparent=True)
plt.savefig('../figures/UPDATED_GD_focal_plane_fs.png', bbox_inches='tight', transparent=True)
plt.tight_layout()
plt.show()
if __name__ == '__main__':
# plt.rc('text', usetex=True)
plt.rc('font', family='serif')
plt.rc('axes', labelsize='large')
# Setup of simulation
l = 1.49e-3
mcd = ParaxialMICADO(n=3001, m=3001, dpix=50)
# mcd.plot_ray_trace()
# Get x3 and y3 (the coordinates to apply the ADC WFE to):
# The focal plane area is about 200x200 mm, so we apply the wavefront error to an area
# of 200x200 mm.
xfp, yfp = np.meshgrid(np.linspace(-150, 150, mcd.n), np.linspace(-150, 150, mcd.m))
# _, xfp, yfp = mcd.propagate(l=l,surface=1, zoom=False, show=False,
# print_progress=False)
# Generate a wavefront error for the intermediate focal plane.
print('\nSetting the wavefront errors...')
wfe_fp = WavefrontError(mcd.n,mcd.m)
wfe_rms = 15000e-6
#PSD Wavefront error
# Loading the phase generated using a Fourier Series
fs_array = load_variable('wfe_fp_phase_array_{}_{}.pkl'.format(mcd.n, mcd.m))
# Retrieving the coefficients for the Fourier series
np.random.seed(2183)
coeff = np.random.uniform(-1,1,(101,101))
wfe_fp.coefficients = coeff
fs_array = fourier_series(xfp, yfp, coeff, Px=30, Py=30, xs=10, ys=10)
# Setting the initial full window phase array to generate the full window surface height map.
wfe_fp.phase = fs_array
surface_map_fp = wfe_fp.generate_height_map(wfe_rms, 2, 1)
# plot_real_valued_results(surface_map_fp*1e6, xfp, yfp, logscale=False, fig_size=(8,6.4), grid=False, show=True,
# colormap='jet', labeltext='Surface height (nm)')
# save_variable('../data/MCD_IFP_wfe_object_PSD_50nm.pkl', wfe_fp)
# wfe_fp = load_variable('../data/MCD_IFP_wfe_object_PSD_50nm.pkl')
# surface_map_fp = wfe_fp.data
# plt.show()
Elist, Eplist = [], []
xlist, ylist = [], []
xplist, yplist = [], []
x_expected_list, y_expected_list = [], []
x_centroid_list, y_centroid_list = [], []
# Set field points
hxList, hyList = np.mgrid[-1:1:11j,-1:1:11j]-(1/300)
# hxList, hyList = np.mgrid[-.1/11:.1/11:11j,-.1/11:.1/11:11j]-(1/300) # Zoomed in to central area
hxList = hxList.flatten()
hyList = hyList.flatten()
# hxList, hyList = [0.0], [0.0]
# List of performed simulations
# Defined by PSD
## GD_IFP : -1:1
## GD_IFP_zoom : -1/11 :1/11
## GD_IFP_zoom2 : -.5/11:.5/11
## GD_IFP_zoom3 : -.1/11:.1/11
# Defined by zernikes
## GD_IFP_obscured_PSF_xcentroid_expected_zern : 50 nm rms 19 zernike modes : -1 : 1
## To do; check PSF shape at center field with zern wfe
## To do; check interpolated WFE at any zoomed in field point.
# Propagate through the field positions without any WFEs
for i in range(len(hxList)):
hx = hxList[i]
hy = hyList[i]
print('Data point: i={:.0f} of i_max={:.0f}'.format(i, len(hxList)))
print('hx: {} - hy: {}'.format(hx,hy) )
# Initializing MICADO paraxial model
mcd = ParaxialMICADO(n=3001, m=3001, dpix=50, entrance_pupil='obscured')
# Propagate through the MICADO system
E, x, y = mcd.propagate(l=l,surface=-1, zoom=False, show=False,
print_progress=False, hx=hx, hy=hy)
# Find approximation for the centroid and amplitude as a first
# guess for the Obscured Airy pattern fit.
amplitude_guess = np.max(np.abs(E))**2
xbc, ybc, _, _ = mcd.centroid_shift(method='Barycenter')
print('Barycenter guess: ', xbc, ybc)
# Do the Obscured Airy Pattern Fit for the unperturbed PSF
xe, ye, _, _ = mcd.centroid_shift(method='Obscured Airy', initialGuess=(amplitude_guess, xbc, ybc, 0.0, 0.03))
print('Airy result: ', xe, ye)
x_expected_list.append(xe)
y_expected_list.append(ye)
# Load in the WFEs and set them to the corresponding ADC surfaces
print('\nSetting the wavefront errors...')
# null_wfe = np.zeros((3001,3001))
mcd.ELT_FP.add_wavefront_error(surface_map_fp, xfp, yfp, wfe=wfe_fp)
# Propagate through the MICADO system
print('Propagating with Wavefront Errors applied...')
Ep, xp, yp = mcd.propagate(l=l,surface=-1, zoom=False, show=True,
print_progress=False, hx=hx, hy=hy)
# Do the Obscured Airy Pattern Fit for the perturbed PSF
xf, yf, _, _ = mcd.centroid_shift(method='Obscured Airy', initialGuess=(amplitude_guess, xe, ye, 0.0, 0.03))
# Resulting centroid shift due to the WFE
x_centroid_list.append(xf)
y_centroid_list.append(yf)
R = np.sqrt( (xf-xe)**2 + (yf-ye)**2 ) * mcd.platescale * 1e6
print('xf: {}, yf: {}, shift (uas): {}'.format(xf,yf, R))
data_tuple = (i, hx, hy, xe, ye, xf, yf)
data_name = 'gd_point_{:03d}_hx{}_hy{}.pkl'.format(i, hx, hy)
save_variable('../data/GD_IFP_intermediate_results/{}'.format(data_name), data_tuple)
# Clear the memory by emptying the large arrays
mcd = None
wfe = None
E, x, y = None, None, None
Ep, xp, yp = None, None, None
# Reshaping the arrays
shape = (int(np.sqrt(len(x_expected_list))), int(np.sqrt(len(x_expected_list))))
x_expected = np.asarray(x_expected_list).reshape(shape)
y_expected = np.asarray(y_expected_list).reshape(shape)#.T
# Saving the arrays
save_variable('../data/GD_IFP_obscured_PSF_xcentroid_expected_fs.pkl', x_expected)
save_variable('../data/GD_IFP_obscured_PSF_ycentroid_expected_fs.pkl', y_expected)
# Reshaping the arrays
shape = (int(np.sqrt(len(x_centroid_list))), int(np.sqrt(len(x_centroid_list))))
x_centroid = np.asarray(x_centroid_list).reshape(shape)
y_centroid = np.asarray(y_centroid_list).reshape(shape)
# Saving the arrays
save_variable('../data/GD_IFP_obscured_PSF_xcentroid_found_with_wfe_airy_fs.pkl', x_centroid)
save_variable('../data/GD_IFP_obscured_PSF_ycentroid_found_with_wfe_airy_fs.pkl', y_centroid)
plot_distortions()
@@ -91,9 +91,10 @@ def convert_detector_size(x, y, old=True):
def plot_distortions(xe, ye, xp, yp, lims=False, add_detectors=True, def plot_distortions(xe, ye, xp, yp, lims=False, add_detectors=True,
save=False, figtitle='geom_distortions', save=False, figtitle='geom_distortions',
show=True, cmap='magma'): show=True, cmap='magma'):
vmin, vmax = 0.1, 100 vmin, vmax = 0.01, 100
mcd = ParaxialMICADO(n=3001, m=3001, dpix=50, l=1.49e-3) mcd = ParaxialMICADO(n=3001, m=3001, dpix=50, l=1.49e-3)
n = xe.shape[0] n = xe.shape[0]
no_of_grids = int(n/121)
X = xe.flatten() X = xe.flatten()
Y = ye.flatten() Y = ye.flatten()
dX = xp - xe dX = xp - xe
@@ -103,7 +104,7 @@ def plot_distortions(xe, ye, xp, yp, lims=False, add_detectors=True,
# Magnitude of the distortions # Magnitude of the distortions
R = np.sqrt(dx**2 + dy**2) * mcd.platescale * 1e6 R = np.sqrt(dx**2 + dy**2) * mcd.platescale * 1e6
print('RMS of the PSF shifts: {:.4f} μas.'.format(np.sqrt(np.mean((R)**2)))) print('RMS of the PSF shifts: {:.4f} μas.'.format(np.sqrt(np.mean((R[:121])**2))))
# Dummy figure, used for the colorbar colors in the quiver plot # Dummy figure, used for the colorbar colors in the quiver plot
# im = plt.imshow(np.reshape(R, (n,n))) # im = plt.imshow(np.reshape(R, (n,n)))
@@ -113,104 +114,160 @@ def plot_distortions(xe, ye, xp, yp, lims=False, add_detectors=True,
plt.close() plt.close()
# Initiate the figure # Initiate the figure
# fig = plt.figure(figsize=(9.6,4.8)) fig = plt.figure(figsize=(12,4.8))
# ax = fig.add_subplot(121, aspect='equal') gs = GridSpec(ncols=3, nrows=2, width_ratios=[2,1,1], height_ratios=[1,1])#left=0.05, right=0.48, wspace=0.05)
# ax2 = fig.add_subplot(122, aspect='equal') ax = fig.add_subplot(gs[:, :1])
fig, axes = plt.subplots(ncols=2, ax2 = fig.add_subplot(gs[0, 1])
nrows=1, ax3 = fig.add_subplot(gs[0, 2])
gridspec_kw={'width_ratios': [1,1]}, ax4 = fig.add_subplot(gs[1, 1])
figsize=(9.6,4.8)) ax5 = fig.add_subplot(gs[1, 2])
ax, ax2 = axes
ax.set_aspect('equal') axilist = [ax, ax2, ax3, ax4, ax5]
ax2.set_aspect('equal') for axi in axilist:
axi.set_aspect('equal')
# Add the detector layout if so desired. # Add the detector layout if so desired.
if add_detectors: if add_detectors:
add_detector_layout((fig,ax), lw=.8, alpha=1, show_label=False) add_detector_layout((fig,ax), lw=1, alpha=1, show_label=False)
# Plot the vector plot # Plot the vector plot
xe = np.asarray(xe[:121]).reshape((11,11)) xe = np.asarray(xe[:121]).reshape((11,11))
ye = np.asarray(ye[:121]).reshape((11,11)) ye = np.asarray(ye[:121]).reshape((11,11))
# for i in range(len(xe)):
# ax.plot(xe[:,i], ye[:,i], 'k-', alpha=0.2, linewidth=.5) # Now we iterate over the different iterations of the GD simulations
# ax.plot(xe[i,:], ye[i,:], 'k-', alpha=0.2, linewidth=.5) print('Total number of grids: {}'.format(no_of_grids))
# ax.quiver(X,Y,dx,dy,R, alpha=1, units='dots', zorder=300) for k in range(no_of_grids):
for k in range(4): # Selecting the different iterations (11x11 grid, but flattened)
print()
ind_end = (k+1) * 121 ind_end = (k+1) * 121
ind_start = k * 121 ind_start = k * 121
dpix = np.diff(Y)[ind_start:ind_end][1] dpix = np.diff(Y)[ind_start:ind_end][1]
print(k, ind_start, ind_end, dpix)
# Coordinates of the grid in this iteration
xplot = np.asarray(X[ind_start:ind_end]).reshape((11,11)) xplot = np.asarray(X[ind_start:ind_end]).reshape((11,11))
yplot = np.asarray(Y[ind_start:ind_end]).reshape((11,11)) yplot = np.asarray(Y[ind_start:ind_end]).reshape((11,11))
yplot, xplot = np.mgrid[yplot.min()+dpix/2:yplot.max()-dpix/2:12j, yplot, xplot = np.mgrid[yplot.min()+dpix/2:yplot.max()-dpix/2:12j,
xplot.min()+dpix/2:xplot.max()-dpix/2:12j] xplot.min()+dpix/2:xplot.max()-dpix/2:12j]
# Magnitude of the distortion at coordinates xplot, yplot
# Used to color the background
Rtemp = np.asarray(R[ind_start:ind_end]).reshape((11,11)) Rtemp = np.asarray(R[ind_start:ind_end]).reshape((11,11))
ax.pcolor(-1*yplot.T, -1*xplot.T, Rtemp.T, cmap=cmap, norm=LogNorm(vmin=vmin, vmax=vmax)) shiftx = X[ind_start:ind_end][61]
# ax2.pcolor(-1*xplot.T, -1*yplot.T, Rtemp.T, cmap=cmap, norm=LogNorm(vmin=1, vmax=100))
shiftx = X[ind_start:ind_end][61]#np.abs(dpix)/2
shifty = Y[ind_start:ind_end][60] shifty = Y[ind_start:ind_end][60]
print(Y[ind_start:ind_end])
print('shiftx:', shiftx)
print('shifty:', shifty)
# ax2.pcolor(k*shifty-1*yplot.T, k*shiftx-1*xplot.T, Rtemp.T*10, cmap=cmap, norm=LogNorm(vmin=1, vmax=100))
if k == 1: ax2.pcolor(shifty-1*yplot.T, shiftx-1*xplot.T, Rtemp.T, cmap=cmap, norm=LogNorm(vmin=vmin, vmax=vmax)) if k in [0,1,2,3,7]: # Plot the center part pcolor
elif k == 2: ax2.pcolor(2*shifty-1*yplot.T, 2*shiftx-1*xplot.T, Rtemp.T, cmap=cmap, norm=LogNorm(vmin=vmin, vmax=vmax)) ax.pcolor(1*shifty-1*yplot.T, 1*shiftx-1*xplot.T, Rtemp.T, cmap=cmap, norm=LogNorm(vmin=vmin, vmax=vmax))
elif k == 3: ax2.pcolor(2*shifty-1*yplot.T, 2*shiftx-1*xplot.T, Rtemp.T, cmap=cmap, norm=LogNorm(vmin=vmin, vmax=vmax)) # if k > 3: # Plot the zoom and megazoom
if k == 0: ax2.pcolor(2*shifty-1*yplot.T, 2*shiftx-1*xplot.T, Rtemp.T, cmap=cmap, norm=LogNorm(vmin=vmin, vmax=vmax))
ax3.pcolor(2*shifty-1*yplot.T, 2*shiftx-1*xplot.T, Rtemp.T, cmap=cmap, norm=LogNorm(vmin=vmin, vmax=vmax))
if k in [0,4,5,6,8]: # Plot the left bottom part pcolor
if k!=0:
ax.pcolor(yplot.T, xplot.T, Rtemp.T, cmap=cmap, norm=LogNorm(vmin=vmin, vmax=vmax))
ax4.pcolor(2*shifty-1*yplot.T, 2*shiftx-1*xplot.T, Rtemp.T, cmap=cmap, norm=LogNorm(vmin=vmin, vmax=vmax))
ax5.pcolor(2*shifty-1*yplot.T, 2*shiftx-1*xplot.T, Rtemp.T, cmap=cmap, norm=LogNorm(vmin=vmin, vmax=vmax))
# Quiver plots
# Full field plot
if k == 0:
ax.quiver(X[ind_start:ind_end],Y[ind_start:ind_end], ax.quiver(X[ind_start:ind_end],Y[ind_start:ind_end],
dx[ind_start:ind_end],dy[ind_start:ind_end], dx[ind_start:ind_end],dy[ind_start:ind_end],
angles='xy', edgecolor='k', facecolor='k', angles='xy', edgecolor='k', facecolor='k',
linewidth=.8, zorder=300, alpha=1, scale=1.4e-5, linewidth=.8, zorder=300, alpha=1, scale=1.2e-5,
units='dots') units='dots')
elif k in [2,3]: # Center plot zoom
if k in [0,1,2]:
ax2.quiver(X[ind_start:ind_end],Y[ind_start:ind_end], ax2.quiver(X[ind_start:ind_end],Y[ind_start:ind_end],
dx[ind_start:ind_end],dy[ind_start:ind_end], dx[ind_start:ind_end],dy[ind_start:ind_end],
angles='xy', edgecolor='k', facecolor='k', angles='xy', edgecolor='k', facecolor='k',
linewidth=.8, zorder=300, alpha=1, scale=0.2e-5, linewidth=.8, zorder=300, alpha=1, scale=7e-7,
units='dots') units='dots')
# ax.pcolor(xplot * (1 - dpix/105), yplot * (1 - dpix/105), Rtemp, cmap='jet', norm=LogNorm(vmin=1, vmax=100))
# Center plot megazoom
ax.set_xlabel('x (mm)', fontsize=16) elif k in [2,3,7]:
ax.set_ylabel('y (mm)', fontsize=16) ax3.quiver(X[ind_start:ind_end],Y[ind_start:ind_end],
ax.tick_params(axis='both', direction='in', labelsize=14, zorder=-1, dx[ind_start:ind_end],dy[ind_start:ind_end],
top=True, right=True) angles='xy', edgecolor='k', facecolor='k',
ax2.set_xlabel('x (mm)', fontsize=16) linewidth=.8, zorder=300, alpha=1, scale=1e-6,
ax2.set_ylabel('y (mm)', fontsize=16) units='dots')
ax2.tick_params(axis='both', direction='in', labelsize=14, zorder=-1,
top=True, right=True) # Left bottom zoom
if k in [4,5]:
ax4.quiver(X[ind_start:ind_end],Y[ind_start:ind_end],
dx[ind_start:ind_end],dy[ind_start:ind_end],
angles='xy', edgecolor='k', facecolor='k',
linewidth=.8, zorder=300, alpha=1, scale=1.2e-5,
units='dots')
if k in [5,6,8]:
ax5.quiver(X[ind_start:ind_end],Y[ind_start:ind_end],
dx[ind_start:ind_end],dy[ind_start:ind_end],
angles='xy', edgecolor='k', facecolor='k',
linewidth=.8, zorder=300, alpha=1, scale=7e-6,
units='dots')
# Adjusting how the axes look in all subfigures
for axi in [ax, ax2, ax4]:
axi.set_ylabel('y (mm)', fontsize=16)
for axi in [ax, ax4, ax5]:
axi.set_xlabel('x (mm)', fontsize=16)
for axi in axilist:
axi.tick_params(axis='both', direction='in', labelsize=14, zorder=-1,
top=True, right=True)
# Setting some limits to the subplots
cb = fig.colorbar(im, ax=axes, fraction=0.025, pad=0.04)
cb.set_label(label='PSF centroid shift (μas)', size=14)
cb.ax.tick_params(labelsize=14)
lbls = 10**np.asarray(cb.ax.get_yticks())
print(lbls)
newlbls = ['{:.0f}'.format(x) for x in 10**np.asarray(cb.ax.get_yticks())]
newlbls[1] = '{:.1f}'.format(lbls[1])
newlbls[0] = '{:.1f}'.format(lbls[0])
cb.ax.set_yticklabels(newlbls)
print(cb.ax.get_yticks())
if not lims: if not lims:
ax.set_xlim([-125, 115]) ax.set_xlim([-125, 115])
ax.set_ylim([-115, 115]) ax.set_ylim([-115, 115])
elif lims: elif lims:
ax.set_xlim(lims[0]) ax.set_xlim(lims[0])
ax.set_ylim(lims[1]) ax.set_ylim(lims[1])
ax2.set_xlim([-2.01,2.75]) ax2.set_xlim([-1+0.3513,1+0.3513])
ax2.set_ylim([-2.01,2.75]) ax2.set_ylim([-1+0.3513,1+0.3513])
ax.indicate_inset_zoom(ax2, edgecolor="black") ax3.set_xlim([-0.01+0.3513, 0.01+0.3513])
ax3.set_ylim([-0.01+0.3513, 0.01+0.3513])
# ax4.set_xlim([-2.01-84,2.75-84])
# ax4.set_ylim([-2.01-84,2.75-84])
ax4.set_xlim([-83.6-1,-83.6+1])
ax4.set_ylim([-83.6-1,-83.6+1])
ax5.set_xlim([-0.01-83.6070, 0.01-83.6070])
ax5.set_ylim([-0.01-83.6070, 0.01-83.6070])
# For the center inset
ax.indicate_inset_zoom(ax2, edgecolor='black')
ax2.indicate_inset_zoom(ax3, edgecolor='black')
# For the off-center inset
ax.indicate_inset_zoom(ax4, edgecolor='black')
ax4.indicate_inset_zoom(ax5, edgecolor='black')
# after plotting the data, format the labels
ax5.set_xticklabels(['{:.2f}'.format(x) for x in ax5.get_xticks()])
ax5.set_yticklabels(['{:.2f}'.format(x) for x in ax5.get_yticks()])
fig.subplots_adjust(top=1.0, fig.subplots_adjust(top=1.0,
bottom=0.0, bottom=0.08,
left=0.1, left=0.075,
right=0.8, right=0.88,
hspace=0.135, hspace=0.145,
wspace=0.3) wspace=0.45)
# Setting and modifying the colorbar
fig.subplots_adjust(right=0.88)
cbar_ax = fig.add_axes([0.9, 0.12, 0.02, 0.85])
cb = fig.colorbar(im, cax=cbar_ax)
cb.set_label(label='PSF centroid shift (μas)', size=14)
cb.ax.tick_params(labelsize=14)
lbls = 10**np.asarray(cb.ax.get_yticks())
newlbls = ['{:.0f}'.format(x) for x in 10**np.asarray(cb.ax.get_yticks())]
newlbls[3] = '{:.1f}'.format(lbls[3])
newlbls[2] = '{:.1f}'.format(lbls[2])
newlbls[1] = '{:.2f}'.format(lbls[1])
newlbls[0] = '{:.2f}'.format(lbls[0])
cb.ax.set_yticklabels(newlbls)
# plt.tight_layout() # plt.tight_layout()
if save: if save:
plt.savefig('{}.pdf'.format(figtitle), plt.savefig('{}.pdf'.format(figtitle),
@@ -228,51 +285,127 @@ def plot_distortions(xe, ye, xp, yp, lims=False, add_detectors=True,
if __name__ == '__main__': if __name__ == '__main__':
ps = ParaxialMICADO(n=3001, m=3001, dpix=50, l=1.49e-3).platescale ps = ParaxialMICADO(n=3001, m=3001, dpix=50, l=1.49e-3).platescale
print(ps)
plt.rc('font', family='serif') plt.rc('font', family='serif')
plt.rc('axes', labelsize='large') plt.rc('axes', labelsize='large')
# convert_detector_size() # convert_detector_size()
# Plotting the original distortions # Plotting the original distortions, -1/11 :1/11
data_dir = '../data/GD Focal Plane WFEs/' data_dir = '../data/GD Focal Plane WFEs/'
xe_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_xcentroid_expected_zern_psd.pkl') xe_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_xcentroid_expected_zern_psd.pkl')
ye_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_ycentroid_expected_zern_psd.pkl') ye_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_ycentroid_expected_zern_psd.pkl')
xp_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl') xp_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl') yp_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
# Zoom 1: # # Zoom 1: -1/11 :1/11
xe_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_xcentroid_expected_zern_psd.pkl') # xe_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_xcentroid_expected_zern_psd.pkl')
ye_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_ycentroid_expected_zern_psd.pkl') # ye_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_ycentroid_expected_zern_psd.pkl')
xp_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl') # xp_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl') # yp_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
# Zoom 2 # # Zoom 2: -.5/11:.5/11
xe_og2 = load_variable(data_dir + 'GD_IFP_zoom2_obscured_PSF_xcentroid_expected_zern_psd.pkl') # xe_og2 = load_variable(data_dir + 'GD_IFP_zoom2_obscured_PSF_xcentroid_expected_zern_psd.pkl')
ye_og2 = load_variable(data_dir + 'GD_IFP_zoom2_obscured_PSF_ycentroid_expected_zern_psd.pkl') # ye_og2 = load_variable(data_dir + 'GD_IFP_zoom2_obscured_PSF_ycentroid_expected_zern_psd.pkl')
xp_og2 = load_variable(data_dir + 'GD_IFP_zoom2_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl') # xp_og2 = load_variable(data_dir + 'GD_IFP_zoom2_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og2 = load_variable(data_dir + 'GD_IFP_zoom2_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl') # yp_og2 = load_variable(data_dir + 'GD_IFP_zoom2_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
# Zoom 3 # # Zoom 3: -.1/11:.1/11
xe_og3 = load_variable(data_dir + 'GD_IFP_zoom3_obscured_PSF_xcentroid_expected_zern_psd.pkl') # xe_og3 = load_variable(data_dir + 'GD_IFP_zoom3_obscured_PSF_xcentroid_expected_zern_psd.pkl')
ye_og3 = load_variable(data_dir + 'GD_IFP_zoom3_obscured_PSF_ycentroid_expected_zern_psd.pkl') # ye_og3 = load_variable(data_dir + 'GD_IFP_zoom3_obscured_PSF_ycentroid_expected_zern_psd.pkl')
xp_og3 = load_variable(data_dir + 'GD_IFP_zoom3_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl') # xp_og3 = load_variable(data_dir + 'GD_IFP_zoom3_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og3 = load_variable(data_dir + 'GD_IFP_zoom3_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl') # yp_og3 = load_variable(data_dir + 'GD_IFP_zoom3_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
xe = np.append(xe_og, [xe_og1, xe_og2, xe_og3]) # Zoom 97: -1/11: 1/11
ye = np.append(ye_og, [ye_og1, ye_og2, ye_og3]) xe_og1 = load_variable(data_dir + 'GD_IFP_zoom97_obscured_PSF_xcentroid_expected_zern_psd.pkl')
xp = np.append(xp_og, [xp_og1, xp_og2, xp_og3]) ye_og1 = load_variable(data_dir + 'GD_IFP_zoom97_obscured_PSF_ycentroid_expected_zern_psd.pkl')
yp = np.append(yp_og, [yp_og1, yp_og2, yp_og3]) xp_og1 = load_variable(data_dir + 'GD_IFP_zoom97_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og1 = load_variable(data_dir + 'GD_IFP_zoom97_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
# xe = np.append(xe_og2, xe_og3) # Zoom 98: -1/121 : 1/121
# ye = np.append(ye_og2, ye_og3) xe_og2 = load_variable(data_dir + 'GD_IFP_zoom98_obscured_PSF_xcentroid_expected_zern_psd.pkl')
# xp = np.append(xp_og2, xp_og3) ye_og2 = load_variable(data_dir + 'GD_IFP_zoom98_obscured_PSF_ycentroid_expected_zern_psd.pkl')
# yp = np.append(yp_og2, yp_og3) xp_og2 = load_variable(data_dir + 'GD_IFP_zoom98_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og2 = load_variable(data_dir + 'GD_IFP_zoom98_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
# Zoom 99: -1/11**3 : -1/11**3
xe_og3 = load_variable(data_dir + 'GD_IFP_zoom99_obscured_PSF_xcentroid_expected_zern_psd.pkl')
ye_og3 = load_variable(data_dir + 'GD_IFP_zoom99_obscured_PSF_ycentroid_expected_zern_psd.pkl')
xp_og3 = load_variable(data_dir + 'GD_IFP_zoom99_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og3 = load_variable(data_dir + 'GD_IFP_zoom99_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
figtitle = '../figures adapted for thesis/Focal Plane/GD_IFP_zern' # Zoom7: -.01/11:.01/11
plot_distortions(xe, ye, xp, yp, show=True, save=True, figtitle=figtitle, xe_og4 = load_variable(data_dir + 'GD_IFP_zoom92_obscured_PSF_xcentroid_expected_zern_psd.pkl')
ye_og4 = load_variable(data_dir + 'GD_IFP_zoom92_obscured_PSF_ycentroid_expected_zern_psd.pkl')
xp_og4 = load_variable(data_dir + 'GD_IFP_zoom92_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og4 = load_variable(data_dir + 'GD_IFP_zoom92_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
# Zoom8: -.05/11:.05/11 - not plotted
# Zoom93: -.004/11:.004/11
xe_og5 = load_variable(data_dir + 'GD_IFP_zoom93_obscured_PSF_xcentroid_expected_zern_psd.pkl')
ye_og5 = load_variable(data_dir + 'GD_IFP_zoom93_obscured_PSF_ycentroid_expected_zern_psd.pkl')
xp_og5 = load_variable(data_dir + 'GD_IFP_zoom93_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og5 = load_variable(data_dir + 'GD_IFP_zoom93_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
# # Zoom91: -.01/11:.01/11 but hx and hy shifted to an area near the corner of the image,
# # where distortion is supposed to be large
# xe_og6 = load_variable(data_dir + 'GD_IFP_zoom91_obscured_PSF_xcentroid_expected_zern_psd.pkl')
# ye_og6 = load_variable(data_dir + 'GD_IFP_zoom91_obscured_PSF_ycentroid_expected_zern_psd.pkl')
# xp_og6 = load_variable(data_dir + 'GD_IFP_zoom91_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
# yp_og6 = load_variable(data_dir + 'GD_IFP_zoom91_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
# Zoom94: -1/11:1/11 but hx and hy shifted to an area near the corner of the image,
# where distortion is supposed to be large
xe_og6 = load_variable(data_dir + 'GD_IFP_zoom94_obscured_PSF_xcentroid_expected_zern_psd.pkl')
ye_og6 = load_variable(data_dir + 'GD_IFP_zoom94_obscured_PSF_ycentroid_expected_zern_psd.pkl')
xp_og6 = load_variable(data_dir + 'GD_IFP_zoom94_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og6 = load_variable(data_dir + 'GD_IFP_zoom94_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
# Zoom95: -1/121:1/121 but hx and hy shifted to an area near the corner of the image,
# where distortion is supposed to be large
xe_og7 = load_variable(data_dir + 'GD_IFP_zoom95_obscured_PSF_xcentroid_expected_zern_psd.pkl')
ye_og7 = load_variable(data_dir + 'GD_IFP_zoom95_obscured_PSF_ycentroid_expected_zern_psd.pkl')
xp_og7 = load_variable(data_dir + 'GD_IFP_zoom95_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og7 = load_variable(data_dir + 'GD_IFP_zoom95_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
# Zoom96: -1/11**3:1/11**3 but hx and hy shifted to an area near the corner of the image,
# where distortion is supposed to be large
xe_og8 = load_variable(data_dir + 'GD_IFP_zoom96_obscured_PSF_xcentroid_expected_zern_psd.pkl')
ye_og8 = load_variable(data_dir + 'GD_IFP_zoom96_obscured_PSF_ycentroid_expected_zern_psd.pkl')
xp_og8 = load_variable(data_dir + 'GD_IFP_zoom96_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og8 = load_variable(data_dir + 'GD_IFP_zoom96_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
# Zoom 100: - 1/11**4:1/11**4, phase interpolation, center of the field
xe_og9 = load_variable(data_dir + 'GD_IFP_zoom100_obscured_PSF_xcentroid_expected_zern_psd.pkl')
ye_og9 = load_variable(data_dir + 'GD_IFP_zoom100_obscured_PSF_ycentroid_expected_zern_psd.pkl')
xp_og9 = load_variable(data_dir + 'GD_IFP_zoom100_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og9 = load_variable(data_dir + 'GD_IFP_zoom100_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
# Zoom 101: - 1/11**4:1/11**4, phase interpolation, center of the field, with shift to left bottom corner
xe_og10 = load_variable(data_dir + 'GD_IFP_zoom101_obscured_PSF_xcentroid_expected_zern_psd.pkl')
ye_og10 = load_variable(data_dir + 'GD_IFP_zoom101_obscured_PSF_ycentroid_expected_zern_psd.pkl')
xp_og10 = load_variable(data_dir + 'GD_IFP_zoom101_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl')
yp_og10 = load_variable(data_dir + 'GD_IFP_zoom101_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl')
# Combining the data points
# xe = np.append(xe_og, [xe_og1, xe_og2, xe_og3, xe_og4, xe_og5, xe_og6, xe_og7, xe_og8])
# ye = np.append(ye_og, [ye_og1, ye_og2, ye_og3, ye_og4, ye_og5, ye_og6, ye_og7, ye_og8])
# xp = np.append(xp_og, [xp_og1, xp_og2, xp_og3, xp_og4, xp_og5, xp_og6, xp_og7, xp_og8])
# yp = np.append(yp_og, [yp_og1, yp_og2, yp_og3, yp_og4, yp_og5, yp_og6, yp_og7, yp_og8])
xe = np.append(xe_og, [xe_og1, xe_og2, xe_og3, xe_og6, xe_og7, xe_og8, xe_og9, xe_og10])
ye = np.append(ye_og, [ye_og1, ye_og2, ye_og3, ye_og6, ye_og7, ye_og8, ye_og9, ye_og10])
xp = np.append(xp_og, [xp_og1, xp_og2, xp_og3, xp_og6, xp_og7, xp_og8, xp_og9, xp_og10])
yp = np.append(yp_og, [yp_og1, yp_og2, yp_og3, yp_og6, yp_og7, yp_og8, yp_og9, yp_og10])
figtitle = '../figures adapted for thesis/Focal Plane/GD_IFP_psd'
plot_distortions(xe, ye, xp, yp, show=False, save=True, figtitle=figtitle,
lims=[[-120,120],[-120,120]]) lims=[[-120,120],[-120,120]])
plt.close()
@@ -199,7 +199,7 @@ def plot_distortions2(xe, ye, xp, yp, lims=False, add_detectors=True,
# ax.plot(xe[i,:], ye[i,:], 'k-', alpha=0.2, linewidth=.5) # ax.plot(xe[i,:], ye[i,:], 'k-', alpha=0.2, linewidth=.5)
# ax.quiver(X,Y,dx,dy,R, alpha=1, units='dots', zorder=300) # ax.quiver(X,Y,dx,dy,R, alpha=1, units='dots', zorder=300)
for k in range(1): for k in range(3):
print() print()
ind_end = (k+1) * 121 ind_end = (k+1) * 121
ind_start = k * 121 ind_start = k * 121
@@ -300,30 +300,30 @@ if __name__ == '__main__':
# convert_detector_size() # convert_detector_size()
# Plotting the original distortions # Plotting the original distortions
data_dir = '../data/GD Focal Plane WFEs/' data_dir = '../data/GD Focal Plane WFEs/'
xe_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_xcentroid_expected_zern.pkl') xe_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_xcentroid_expected_psd.pkl')
ye_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_ycentroid_expected_zern.pkl') ye_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_ycentroid_expected_psd.pkl')
xp_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_xcentroid_found_with_wfe_airy_zern.pkl') xp_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_xcentroid_found_with_wfe_airy_psd.pkl')
yp_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_ycentroid_found_with_wfe_airy_zern.pkl') yp_og = load_variable(data_dir + 'GD_IFP_obscured_PSF_ycentroid_found_with_wfe_airy_psd.pkl')
# # Zoom 1: # # Zoom 1:
# xe_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_xcentroid_expected_zern.pkl') xe_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_xcentroid_expected_psd.pkl')
# ye_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_ycentroid_expected_zern.pkl') ye_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_ycentroid_expected_psd.pkl')
# xp_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_xcentroid_found_with_wfe_airy_zern.pkl') xp_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_xcentroid_found_with_wfe_airy_psd.pkl')
# yp_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_ycentroid_found_with_wfe_airy_zern.pkl') yp_og1 = load_variable(data_dir + 'GD_IFP_zoom_obscured_PSF_ycentroid_found_with_wfe_airy_psd.pkl')
xe = xe_og#np.append(xe_og, xe_og1) xe = np.append(xe_og, xe_og1)
ye = ye_og#np.append(ye_og, ye_og1) ye = np.append(ye_og, ye_og1)
xp = xp_og#np.append(xp_og, xp_og1) xp = np.append(xp_og, xp_og1)
yp = yp_og#np.append(yp_og, yp_og1) yp = np.append(yp_og, yp_og1)
# xe = np.append(xe_og2, xe_og3) xe = np.append(xe_og2, xe_og3)
# ye = np.append(ye_og2, ye_og3) ye = np.append(ye_og2, ye_og3)
# xp = np.append(xp_og2, xp_og3) xp = np.append(xp_og2, xp_og3)
# yp = np.append(yp_og2, yp_og3) yp = np.append(yp_og2, yp_og3)
figtitle = '../figures adapted for thesis/Focal Plane/GD_IFP_zern' figtitle = '../figures adapted for thesis/Focal Plane/GD_IFP_zern2'
plot_distortions(xe, ye, xp, yp, show=True, save=False, figtitle=figtitle, plot_distortions2(xe, ye, xp, yp, show=True, save=False, figtitle=figtitle,
lims=[[-120,120],[-120,120]]) lims=[[-120,120],[-120,120]])
@@ -0,0 +1,86 @@
import numpy as np
import matplotlib.pyplot as plt
from numpy.polynomial import polynomial
import sys
sys.path.insert(0, '/Users/joost/Dropbox/Werk/PhD/Documents/Published/FourierOptics GitHub release/')
from fourierOptics import *
from pdb import set_trace
def polyfit2d(x, y, z, deg):
x = np.asarray(x)
y = np.asarray(y)
z = np.asarray(z)
deg = np.asarray(deg)
vander = polynomial.polyvander2d(x, y, deg)
vander = vander.reshape((-1,vander.shape[-1])) # Wat gebeurt hier? reshape(-1) lijkt niets te doen?
z = z.reshape((vander.shape[0],))
c = np.linalg.lstsq(vander, z, rcond=None)[0]
return c.reshape(deg+1)
if __name__ == '__main__':
plt.rc('text', usetex=True)
plt.rc('font', family='serif')
# Initializing MICADO paraxial model
mcd = ParaxialMICADO(n=3001, m=3001, dpix=50)
wfe = WavefrontError(mcd.n, mcd.m)
# Setup
l = 1.49e-3
# Set field points
hxList = [0]
hyList = [0]
wfe_p1 = load_variable('../data/compressed items/ADC_tWFE_p1_zern_20nm_psd_15nm.pkl')
wfe_p2 = load_variable('../data/compressed items/ADC_tWFE_p2_zern_20nm_psd_15nm.pkl')
wfe_p3 = load_variable('../data/compressed items/ADC_tWFE_p3_zern_20nm_psd_15nm.pkl')
wfe_p4 = load_variable('../data/compressed items/ADC_tWFE_p4_zern_20nm_psd_15nm.pkl')
# wfe_p1 = load_variable('../data/compressed items/ADC_tWFE_p1_psd_25nm.pkl')
# wfe_p2 = load_variable('../data/compressed items/ADC_tWFE_p2_psd_25nm.pkl')
# wfe_p3 = load_variable('../data/compressed items/ADC_tWFE_p3_psd_25nm.pkl')
# wfe_p4 = load_variable('../data/compressed items/ADC_tWFE_p4_psd_25nm.pkl')
prop_surf = 4
E, x, y = mcd.propagate(l=l,surface=prop_surf, zoom=False, show=False,
print_progress=True, hx=0, hy=0)
# Save the resulting 'perfect' PSF for later
tx_combined = 0
ty_combined = 0
print(mcd.ELT_ADC.p1.CA)
for wfe_surf in [wfe_p1, wfe_p2, wfe_p3, wfe_p4]:
# Don't show anything outside the clear aperture.
mask = np.sqrt(x**2 + y**2) > 40#mcd.ELT_ADC.p1.CA/2
# Plot P1
phasescreenMasked = wfe_surf.copy()
wfe_p1[mask] = np.nan
phasescreenMasked[mask] = 0
# Bepaling van de gemiddelde tilt voor deze coordinaten, i.e. een lineaire fit.
pf1 = polyfit2d(x.real[~mask], y.real[~mask], phasescreenMasked[~mask], [1,1])
# Coefficienten van de fit vertaalt naar graden tilt
o1x = np.arctan(pf1[1,0])
o1y = np.arctan(pf1[0,1])
print(o1x, o1y)
o1x_deg = np.degrees(o1x)
o1y_deg = np.degrees(o1y)
tx_combined += o1x
ty_combined += o1y
print('tx: {} rad ({} deg = {:.2f} arcsec)'.format(o1x, o1x_deg, o1x_deg*3600))
print('ty: {} rad ({} deg = {:.2f} arcsec)'.format(o1y, o1y_deg, o1y_deg*3600))
print('Combined results:\n-----------------')
print('tx: {} rad --> distortion of {} μas'.format(tx_combined, 1601.688*tx_combined*0.267*1e6))
print('ty: {} rad --> distortion of {} μas'.format(ty_combined, 1601.688*ty_combined*0.267*1e6))
tr_combined = np.sqrt(tx_combined**2 + ty_combined**2)
print('tr: {} rad --> distortion of {} μas'.format(tr_combined, 1601.688*tr_combined*0.267*1e6))
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 145 KiB