-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEll_zernike.py
118 lines (94 loc) · 3.58 KB
/
Ell_zernike.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#!/usr/bin/env python3
import numpy as np
from zernike import RZern
import matplotlib.pyplot as plt
from scipy.linalg import eig
class ZernikeEllipticalaperture:
def __init__(self, rmax, npix, a, b, l, ell_aperture=True, coeff=None):
self.rmax = rmax
self.npix = npix
self.a = a
self.b = b
self.l = l
self.ell_aperture = ell_aperture # Specify whether to use the elliptical aperture
self.coeff = coeff # Coefficients for the Zernike modes (optional)
self.ell_aperture_mask = self.GenerateEllipticalAperture()
self.circular_zern = self.GetCircZernikeValue()
self.E = self.CalculateEllipticalZernike()
def GetCircZernikeValue(self):
zernike = RZern(self.rmax)
xx, yy = np.meshgrid(np.linspace(-1, 1, self.npix), np.linspace(-1, 1, self.npix))
rho = np.sqrt(xx**2 + yy**2)
theta = np.arctan2(yy, xx)
zernike.make_cart_grid(xx, yy)
zern_value = []
nterms = int((self.rmax + 1) * (self.rmax + 2) / 2)
for i in range(nterms):
zern_value.append(zernike.Zk(i, rho, theta))
zern_value = np.array(zern_value / np.linalg.norm(zern_value)).squeeze()
return zern_value
def CalculateEllipticalZernike(self):
Z = self.GetCircZernikeValue()
M = self.M_matrix()
E = [] # Initialize a list to store E arrays for each l
for i in range(1, self.l + 1):
E_l = np.zeros(Z[0].shape) # Initialize E with the same shape as Z[0]
for j in range(1, i + 1):
E_l += M[i - 1, j - 1] * Z[j - 1]
E.append(E_l)
E = np.array(E)
if self.ell_aperture:
E[:, np.logical_not(self.ell_aperture_mask)] = 0
return E
def M_matrix(self):
C = self.C_zern()
regularization = 1e-6 # Small positive constant to regularize
C += regularization * np.eye(C.shape[0])
Q = np.linalg.cholesky(C)
QT = np.transpose(Q)
M = np.linalg.inv(QT)
return M
def C_zern(self):
nterms = int((self.rmax + 1) * (self.rmax + 2) / 2)
# Initialize the C matrix
C = np.zeros((nterms, nterms))
# Calculate the area of each grid cell
dx = (2 * self.a) / 10000
dy = (2 * self.b) / 10000
for i in range(nterms):
for j in range(i, nterms):
product_Zern = np.dot(self.circular_zern[i], self.circular_zern[j]) * dx * dy
C[i, j] += np.sum(product_Zern)
if i != j:
C[j, i] = C[i, j]
return C
def GenerateEllipticalAperture(self):
x, y = np.meshgrid(np.linspace(-1, 1, self.npix), np.linspace(-1, 1, self.npix))
normalized_distance = (x / self.a) ** 2 + (y / self.b) ** 2
aperture = (normalized_distance <= 1).astype(float)
return aperture
def EllZernikeMap(self, coeff=None):
xx, yy = np.meshgrid(np.linspace(-1, 1, self.npix), np.linspace(-1, 1, self.npix))
E_ell = np.zeros((xx.size, self.l))
for k in range(self.l):
E_ell[:, k] = np.ravel(self.E[k])
if coeff is None:
coeff = np.random.random(self.l)
phi = np.dot(E_ell, coeff)
phi = phi.reshape(xx.shape)
return phi
'''
if __name__ == '__main__':
a = 1
b = 0.8
rmax = 7
npix = 256
l = 35
ell_zern = ZernikeEllipticalaperture(rmax, npix, a, b, l)
Ell = ell_zern.CalculateEllipticalZernike()
plt.imshow(Ell[11])
plt.show()
phi = ell_zern.EllZernikeMap()
plt.imshow(phi)
plt.show()
'''