ElectroMagneticPython 2.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,423 @@
1
+ """Transfer matrix for isotropic and anisotropic multilayer.
2
+
3
+ The transfer matrix algorithms is used to compute the power reflected
4
+ and transmitted by a multilayer.
5
+ The multilayer can be made of isotropic and anisotropic layers, with
6
+ any thickness.
7
+ Two versions of the algorithm are present: an isotropic one and an
8
+ anisotropic one.
9
+ """
10
+ __author__ = "Lorenzo Bolla"
11
+
12
+ import scipy as S
13
+ from scipy.linalg import inv
14
+ from EMpy.utils import snell, norm
15
+ from EMpy.constants import c, mu0
16
+
17
+ # import Gnuplot
18
+
19
+
20
+ class TransferMatrix:
21
+
22
+ """Class to handle the transfer matrix solvers."""
23
+
24
+ def __init__(self, multilayer):
25
+ """Set the multilayer.
26
+
27
+ INPUT
28
+ multilayer = Multilayer obj describing the sequence of layers.
29
+ """
30
+ self.setMultilayer(multilayer)
31
+
32
+ def setMultilayer(self, m):
33
+ self.multilayer = m.simplify()
34
+
35
+
36
+ class IsotropicTransferMatrix(TransferMatrix):
37
+ def __init__(self, multilayer, theta_inc):
38
+ """Set the multilayer and the incident angle.
39
+
40
+ INPUT
41
+ multilayer = Multilayer obj describing the sequence of layers.
42
+ theta_inc = angle of the incident wave (in radiant) wrt the
43
+ normal.
44
+ """
45
+ if not multilayer.isIsotropic():
46
+ raise ValueError(
47
+ "Cannot use IsotropicTransferMatrix with anisotropic multilayer"
48
+ )
49
+ TransferMatrix.__init__(self, multilayer)
50
+ self.theta_inc = theta_inc
51
+
52
+ def solve(self, wls):
53
+ """Isotropic solver.
54
+
55
+ INPUT
56
+ wls = wavelengths to scan (any asarray-able object).
57
+
58
+ OUTPUT
59
+ self.Rs, self.Ts, self.Rp, self.Tp = power reflected and
60
+ transmitted on s and p polarizations.
61
+ """
62
+
63
+ self.wls = S.asarray(wls)
64
+
65
+ multilayer = self.multilayer
66
+ theta_inc = self.theta_inc
67
+
68
+ nlayers = len(multilayer)
69
+ d = S.array([l.thickness for l in multilayer]).ravel()
70
+
71
+ Rs = S.zeros_like(self.wls)
72
+ Ts = S.zeros_like(self.wls)
73
+ Rp = S.zeros_like(self.wls)
74
+ Tp = S.zeros_like(self.wls)
75
+
76
+ Dp = S.zeros((2, 2), dtype=complex)
77
+ Ds = S.zeros((2, 2), dtype=complex)
78
+ P = S.zeros((2, 2), dtype=complex)
79
+ Ms = S.zeros((2, 2), dtype=complex)
80
+ Mp = S.zeros((2, 2), dtype=complex)
81
+ k = S.zeros((nlayers, 2), dtype=complex)
82
+
83
+ ntot = S.zeros((self.wls.size, nlayers), dtype=complex)
84
+ for i, l in enumerate(multilayer):
85
+ # ntot[:,i] = l.mat.n(self.wls,l.mat.T0)
86
+ ntot[:, i] = l.mat.n(self.wls, l.mat.toc.T0)
87
+
88
+ for iwl, wl in enumerate(self.wls):
89
+
90
+ n = ntot[iwl, :]
91
+ theta = snell(theta_inc, n)
92
+
93
+ k[:, 0] = 2 * S.pi * n / wl * S.cos(theta)
94
+ k[:, 1] = 2 * S.pi * n / wl * S.sin(theta)
95
+
96
+ Ds = [[1.0, 1.0], [n[0] * S.cos(theta[0]), -n[0] * S.cos(theta[0])]]
97
+ Dp = [[S.cos(theta[0]), S.cos(theta[0])], [n[0], -n[0]]]
98
+ Ms = inv(Ds)
99
+ Mp = inv(Dp)
100
+
101
+ for nn, dd, tt, kk in zip(n[1:-1], d[1:-1], theta[1:-1], k[1:-1, 0]):
102
+
103
+ Ds = [[1.0, 1.0], [nn * S.cos(tt), -nn * S.cos(tt)]]
104
+ Dp = [[S.cos(tt), S.cos(tt)], [nn, -nn]]
105
+ phi = kk * dd
106
+ P = [[S.exp(1j * phi), 0], [0, S.exp(-1j * phi)]]
107
+ Ms = S.dot(Ms, S.dot(Ds, S.dot(P, inv(Ds))))
108
+ Mp = S.dot(Mp, S.dot(Dp, S.dot(P, inv(Dp))))
109
+
110
+ Ds = [[1.0, 1.0], [n[-1] * S.cos(theta[-1]), -n[-1] * S.cos(theta[-1])]]
111
+ Dp = [[S.cos(theta[-1]), S.cos(theta[-1])], [n[-1], -n[-1]]]
112
+ Ms = S.dot(Ms, Ds)
113
+ Mp = S.dot(Mp, Dp)
114
+
115
+ rs = Ms[1, 0] / Ms[0, 0]
116
+ ts = 1.0 / Ms[0, 0]
117
+
118
+ rp = Mp[1, 0] / Mp[0, 0]
119
+ tp = 1.0 / Mp[0, 0]
120
+
121
+ Rs[iwl] = S.absolute(rs) ** 2
122
+ Ts[iwl] = (
123
+ S.absolute((n[-1] * S.cos(theta[-1])) / (n[0] * S.cos(theta[0])))
124
+ * S.absolute(ts) ** 2
125
+ )
126
+ Rp[iwl] = S.absolute(rp) ** 2
127
+ Tp[iwl] = (
128
+ S.absolute((n[-1] * S.cos(theta[-1])) / (n[0] * S.cos(theta[0])))
129
+ * S.absolute(tp) ** 2
130
+ )
131
+
132
+ self.Rs = Rs
133
+ self.Ts = Ts
134
+ self.Rp = Rp
135
+ self.Tp = Tp
136
+ return self
137
+
138
+ # def plot(self):
139
+ # """Plot the solution."""
140
+ # g = Gnuplot.Gnuplot()
141
+ # g('set xlabel "$\lambda$"')
142
+ # g('set ylabel "power"')
143
+ # g('set yrange [0:1]')
144
+ # g('set data style linespoints')
145
+ # g.plot(Gnuplot.Data(self.wls, self.Rs, with_ = 'linespoints', title = 'Rs'), \
146
+ # Gnuplot.Data(self.wls, self.Ts, with_ = 'linespoints', title = 'Ts'), \
147
+ # Gnuplot.Data(self.wls, self.Rp, with_ = 'linespoints', title = 'Rp'), \
148
+ # Gnuplot.Data(self.wls, self.Tp, with_ = 'linespoints', title = 'Tp'))
149
+ # raw_input('press enter to close the graph...')
150
+
151
+ def __str__(self):
152
+ return "ISOTROPIC TRANSFER MATRIX SOLVER\n\n%s\n\ntheta inc = %g" % (
153
+ self.multilayer.__str__(),
154
+ self.theta_inc,
155
+ )
156
+
157
+
158
+ class AnisotropicTransferMatrix(TransferMatrix):
159
+ def __init__(self, multilayer, theta_inc_x, theta_inc_y):
160
+ """Set the multilayer and the incident angle.
161
+
162
+ INPUT
163
+ multilayer = Multilayer obj describing the sequence of layers.
164
+ theta_inc_x and theta_inc_y = angles of the incident wave (in
165
+ radiant) wrt the normal.
166
+ """
167
+ TransferMatrix.__init__(self, multilayer)
168
+ self.theta_inc_x = theta_inc_x
169
+ self.theta_inc_y = theta_inc_y
170
+
171
+ def solve(self, wls):
172
+ """Anisotropic solver.
173
+
174
+ INPUT
175
+ wls = wavelengths to scan (any asarray-able object).
176
+
177
+ OUTPUT
178
+ self.R, self.T = power reflected and transmitted.
179
+ """
180
+
181
+ self.wls = S.asarray(wls)
182
+
183
+ multilayer = self.multilayer
184
+ theta_inc_x = self.theta_inc_x
185
+ theta_inc_y = self.theta_inc_y
186
+
187
+ def find_roots(wl, epsilon, alpha, beta):
188
+ """Find roots of characteristic equation.
189
+
190
+ Given a wavelength, a 3x3 tensor epsilon and the tangential components
191
+ of the wavevector k = (alpha,beta,gamma_i), returns the 4 possible
192
+ gamma_i, i = 1,2,3,4 that satisfy the boundary conditions.
193
+ """
194
+
195
+ omega = 2.0 * S.pi * c / wl
196
+ K = omega**2 * mu0 * epsilon
197
+
198
+ k0 = 2.0 * S.pi / wl
199
+ K /= k0**2
200
+ alpha /= k0
201
+ beta /= k0
202
+
203
+ alpha2 = alpha**2
204
+ alpha3 = alpha**3
205
+ alpha4 = alpha**4
206
+ beta2 = beta**2
207
+ beta3 = beta**3
208
+ beta4 = beta**4
209
+
210
+ coeff = [
211
+ K[2, 2],
212
+ alpha * (K[0, 2] + K[2, 0]) + beta * (K[1, 2] + K[2, 1]),
213
+ alpha2 * (K[0, 0] + K[2, 2])
214
+ + alpha * beta * (K[1, 0] + K[0, 1])
215
+ + beta2 * (K[1, 1] + K[2, 2])
216
+ + (
217
+ K[0, 2] * K[2, 0]
218
+ + K[1, 2] * K[2, 1]
219
+ - K[0, 0] * K[2, 2]
220
+ - K[1, 1] * K[2, 2]
221
+ ),
222
+ alpha3 * (K[0, 2] + K[2, 0])
223
+ + beta3 * (K[1, 2] + K[2, 1])
224
+ + alpha2 * beta * (K[1, 2] + K[2, 1])
225
+ + alpha * beta2 * (K[0, 2] + K[2, 0])
226
+ + alpha
227
+ * (
228
+ K[0, 1] * K[1, 2]
229
+ + K[1, 0] * K[2, 1]
230
+ - K[0, 2] * K[1, 1]
231
+ - K[2, 0] * K[1, 1]
232
+ )
233
+ + beta
234
+ * (
235
+ K[0, 1] * K[2, 0]
236
+ + K[1, 0] * K[0, 2]
237
+ - K[0, 0] * K[1, 2]
238
+ - K[0, 0] * K[2, 1]
239
+ ),
240
+ alpha4 * (K[0, 0])
241
+ + beta4 * (K[1, 1])
242
+ + alpha3 * beta * (K[0, 1] + K[1, 0])
243
+ + alpha * beta3 * (K[0, 1] + K[1, 0])
244
+ + alpha2 * beta2 * (K[0, 0] + K[1, 1])
245
+ + alpha2
246
+ * (
247
+ K[0, 1] * K[1, 0]
248
+ + K[0, 2] * K[2, 0]
249
+ - K[0, 0] * K[2, 2]
250
+ - K[0, 0] * K[1, 1]
251
+ )
252
+ + beta2
253
+ * (
254
+ K[0, 1] * K[1, 0]
255
+ + K[1, 2] * K[2, 1]
256
+ - K[0, 0] * K[1, 1]
257
+ - K[1, 1] * K[2, 2]
258
+ )
259
+ + alpha
260
+ * beta
261
+ * (
262
+ K[0, 2] * K[2, 1]
263
+ + K[2, 0] * K[1, 2]
264
+ - K[0, 1] * K[2, 2]
265
+ - K[1, 0] * K[2, 2]
266
+ )
267
+ + K[0, 0] * K[1, 1] * K[2, 2]
268
+ - K[0, 0] * K[1, 2] * K[2, 1]
269
+ - K[1, 0] * K[0, 1] * K[2, 2]
270
+ + K[1, 0] * K[0, 2] * K[2, 1]
271
+ + K[2, 0] * K[0, 1] * K[1, 2]
272
+ - K[2, 0] * K[0, 2] * K[1, 1],
273
+ ]
274
+
275
+ gamma = S.roots(coeff)
276
+ tmp = S.sort_complex(gamma)
277
+ gamma = tmp[[3, 0, 2, 1]] # convention
278
+
279
+ k = (
280
+ k0
281
+ * S.array(
282
+ [alpha * S.ones(gamma.shape), beta * S.ones(gamma.shape), gamma]
283
+ ).T
284
+ )
285
+ v = S.zeros((4, 3), dtype=complex)
286
+
287
+ for i, g in enumerate(gamma):
288
+
289
+ # H = K + [
290
+ # [-beta2 - g ** 2, alpha * beta, alpha * g],
291
+ # [alpha * beta, -alpha2 - g ** 2, beta * g],
292
+ # [alpha * g, beta * g, -alpha2 - beta2],
293
+ # ]
294
+ v[i, :] = [
295
+ (K[1, 1] - alpha2 - g**2) * (K[2, 2] - alpha2 - beta2)
296
+ - (K[1, 2] + beta * g) ** 2,
297
+ (K[1, 2] + beta * g) * (K[2, 0] + alpha * g)
298
+ - (K[0, 1] + alpha * beta) * (K[2, 2] - alpha2 - beta2),
299
+ (K[0, 1] + alpha * beta) * (K[1, 2] + beta * g)
300
+ - (K[0, 2] + alpha * g) * (K[1, 1] - alpha2 - g**2),
301
+ ]
302
+
303
+ p3 = v[0, :]
304
+ p3 /= norm(p3)
305
+ p4 = v[1, :]
306
+ p4 /= norm(p4)
307
+ p1 = S.cross(p3, k[0, :])
308
+ p1 /= norm(p1)
309
+ p2 = S.cross(p4, k[1, :])
310
+ p2 /= norm(p2)
311
+
312
+ p = S.array([p1, p2, p3, p4])
313
+ q = wl / (2.0 * S.pi * mu0 * c) * S.cross(k, p)
314
+
315
+ return k, p, q
316
+
317
+ nlayers = len(multilayer)
318
+ d = S.asarray([l.thickness for l in multilayer])
319
+
320
+ # R and T are real, because they are powers
321
+ # r and t are complex!
322
+ R = S.zeros((2, 2, self.wls.size))
323
+ T = S.zeros((2, 2, self.wls.size))
324
+
325
+ epstot = S.zeros((3, 3, self.wls.size, nlayers), dtype=complex)
326
+ for i, l in enumerate(multilayer):
327
+ epstot[:, :, :, i] = l.mat.epsilonTensor(self.wls)
328
+
329
+ for iwl, wl in enumerate(self.wls):
330
+
331
+ epsilon = epstot[:, :, iwl, :]
332
+
333
+ kx = 2 * S.pi / wl * S.sin(theta_inc_x)
334
+ ky = 2 * S.pi / wl * S.sin(theta_inc_y)
335
+ x = S.array([1, 0, 0], dtype=float)
336
+ y = S.array([0, 1, 0], dtype=float)
337
+ # z = S.array([0, 0, 1], dtype=float)
338
+ k = S.zeros((4, 3, nlayers), dtype=complex)
339
+ p = S.zeros((4, 3, nlayers), dtype=complex)
340
+ q = S.zeros((4, 3, nlayers), dtype=complex)
341
+ D = S.zeros((4, 4, nlayers), dtype=complex)
342
+ P = S.zeros((4, 4, nlayers), dtype=complex)
343
+
344
+ for i in range(nlayers):
345
+
346
+ k[:, :, i], p[:, :, i], q[:, :, i] = find_roots(
347
+ wl, epsilon[:, :, i], kx, ky
348
+ )
349
+ D[:, :, i] = [
350
+ [
351
+ S.dot(x, p[0, :, i]),
352
+ S.dot(x, p[1, :, i]),
353
+ S.dot(x, p[2, :, i]),
354
+ S.dot(x, p[3, :, i]),
355
+ ],
356
+ [
357
+ S.dot(y, q[0, :, i]),
358
+ S.dot(y, q[1, :, i]),
359
+ S.dot(y, q[2, :, i]),
360
+ S.dot(y, q[3, :, i]),
361
+ ],
362
+ [
363
+ S.dot(y, p[0, :, i]),
364
+ S.dot(y, p[1, :, i]),
365
+ S.dot(y, p[2, :, i]),
366
+ S.dot(y, p[3, :, i]),
367
+ ],
368
+ [
369
+ S.dot(x, q[0, :, i]),
370
+ S.dot(x, q[1, :, i]),
371
+ S.dot(x, q[2, :, i]),
372
+ S.dot(x, q[3, :, i]),
373
+ ],
374
+ ]
375
+
376
+ for i in range(1, nlayers - 1):
377
+ P[:, :, i] = S.diag(S.exp(1j * k[:, 2, i] * d[i]))
378
+
379
+ M = inv(D[:, :, 0])
380
+ for i in range(1, nlayers - 1):
381
+ M = S.dot(M, S.dot(D[:, :, i], S.dot(P[:, :, i], inv(D[:, :, i]))))
382
+ M = S.dot(M, D[:, :, -1])
383
+
384
+ deltaM = M[0, 0] * M[2, 2] - M[0, 2] * M[2, 0]
385
+
386
+ # reflectance matrix (from yeh_electromagnetic)
387
+ # r = [rss rsp; rps rpp]
388
+ r = (
389
+ S.array(
390
+ [
391
+ [
392
+ M[1, 0] * M[2, 2] - M[1, 2] * M[2, 0],
393
+ M[3, 0] * M[2, 2] - M[3, 2] * M[2, 0],
394
+ ],
395
+ [
396
+ M[0, 0] * M[1, 2] - M[1, 0] * M[0, 2],
397
+ M[0, 0] * M[3, 2] - M[3, 0] * M[0, 2],
398
+ ],
399
+ ],
400
+ dtype=complex,
401
+ )
402
+ / deltaM
403
+ )
404
+
405
+ # transmittance matrix (from yeh_electromagnetic)
406
+ # t = [tss tsp; tps tpp]
407
+ t = S.array([[M[2, 2], -M[2, 0]], [-M[0, 2], M[0, 0]]]) / deltaM
408
+
409
+ # P_t/P_inc = |E_t|**2/|E_inc|**2 . k_t_z/k_inc_z
410
+ T[:, :, iwl] = (S.absolute(t) ** 2 * k[0, 2, -1] / k[0, 2, 0]).real
411
+
412
+ # P_r/P_inc = |E_r|**2/|E_inc|**2
413
+ R[:, :, iwl] = S.absolute(r) ** 2
414
+
415
+ self.R = R
416
+ self.T = T
417
+ return self
418
+
419
+ def __str__(self):
420
+ return (
421
+ "ANISOTROPIC TRANSFER MATRIX SOLVER\n\n%s\n\ntheta inc x = %g\ntheta inc y = %g"
422
+ % (self.multilayer.__str__(), self.theta_inc_x, self.theta_inc_y)
423
+ )