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.
EMpy/RCWA.py ADDED
@@ -0,0 +1,885 @@
1
+ """Rigorous Coupled Wave Analysis.
2
+
3
+ The algorithm, described in Glytsis, "Three-dimensional (vector)
4
+ rigorous coupled-wave analysis of anisotropic grating diffraction",
5
+ JOSA A, 7(8), 1990, permits to find the diffraction efficiencies
6
+ (i.e. the power normalized to unity) reflected and transmitted by a
7
+ multilayer.
8
+ The multilayer can be made of isotropic or anisotropic layers and
9
+ binary gratings.
10
+ Two versions of the algorithm are present: an isotropic one, stable
11
+ for every diffraction order and layer thickness, and an anisotropic
12
+ one, only stable for low diffraction orders and thin layers.
13
+
14
+ """
15
+ __author__ = "Lorenzo Bolla"
16
+
17
+ import scipy as S
18
+ from scipy.linalg import toeplitz, inv, eig, solve as linsolve
19
+ from numpy import pi
20
+
21
+ from EMpy.utils import (
22
+ cond,
23
+ warning,
24
+ BinaryGrating,
25
+ SymmetricDoubleGrating,
26
+ AsymmetricDoubleGrating,
27
+ )
28
+
29
+
30
+ def dispersion_relation_ordinary(kx, ky, k, nO):
31
+ """Dispersion relation for the ordinary wave.
32
+
33
+ NOTE
34
+ See eq. 15 in Glytsis, "Three-dimensional (vector) rigorous
35
+ coupled-wave analysis of anisotropic grating diffraction",
36
+ JOSA A, 7(8), 1990 Always give positive real or negative
37
+ imaginary.
38
+ """
39
+
40
+ if kx.shape != ky.shape:
41
+ raise ValueError("kx and ky must have the same length")
42
+
43
+ delta = (k * nO) ** 2 - (kx**2 + ky**2)
44
+ kz = S.sqrt(delta)
45
+
46
+ # Adjust sign of real/imag part
47
+ kz.real = abs(kz.real)
48
+ kz.imag = -abs(kz.imag)
49
+
50
+ return kz
51
+
52
+
53
+ def dispersion_relation_extraordinary(kx, ky, k, nO, nE, c):
54
+ """Dispersion relation for the extraordinary wave.
55
+
56
+ NOTE
57
+ See eq. 16 in Glytsis, "Three-dimensional (vector) rigorous
58
+ coupled-wave analysis of anisotropic grating diffraction",
59
+ JOSA A, 7(8), 1990 Always give positive real or negative
60
+ imaginary.
61
+ """
62
+
63
+ if kx.shape != ky.shape or c.size != 3:
64
+ raise ValueError(
65
+ "kx and ky must have the same length and c must have 3 components"
66
+ )
67
+
68
+ kz = S.empty_like(kx)
69
+
70
+ for ii in range(0, kx.size):
71
+
72
+ alpha = nE**2 - nO**2
73
+ beta = kx[ii] / k * c[0] + ky[ii] / k * c[1]
74
+
75
+ # coeffs
76
+ C = S.array(
77
+ [
78
+ nO**2 + c[2] ** 2 * alpha,
79
+ 2.0 * c[2] * beta * alpha,
80
+ nO**2 * (kx[ii] ** 2 + ky[ii] ** 2) / k**2
81
+ + alpha * beta**2
82
+ - nO**2 * nE**2,
83
+ ]
84
+ )
85
+
86
+ # two solutions of type +x or -x, purely real or purely imag
87
+ tmp_kz = k * S.roots(C)
88
+
89
+ # get the negative imaginary part or the positive real one
90
+ if S.any(S.isreal(tmp_kz)):
91
+ kz[ii] = S.absolute(tmp_kz[0])
92
+ else:
93
+ kz[ii] = -1j * S.absolute(tmp_kz[0])
94
+
95
+ return kz
96
+
97
+
98
+ class RCWA:
99
+
100
+ """Class to handle the RCWA solvers.
101
+
102
+ NOTE
103
+ See Glytsis, "Three-dimensional (vector) rigorous coupled-wave
104
+ analysis of anisotropic grating diffraction", JOSA A, 7(8), 1990
105
+
106
+ The following variables, used throughout the code, have the following
107
+ meaning:
108
+
109
+ alpha: float
110
+ angle between wave vector k1 and xy plane, in radians
111
+
112
+ delta: float
113
+ angle between the y axis and the projection of k1 onto the xy
114
+ plane, in radians
115
+
116
+ psi: angle between the D vector of the plane wave and the xy plane,
117
+ in radians, TM: 0, TE: numpy.pi / 2
118
+
119
+ phi: angle between the grating vector K and y axis (in the xy plane),
120
+ in radians, the grating is modulated in the direction of the
121
+ grating vector.
122
+
123
+ """
124
+
125
+ def __init__(self, multilayer, alpha, delta, psi, phi, n):
126
+ """Set the multilayer, the angles of incidence and the diffraction order.
127
+
128
+ INPUT
129
+ multilayer = Multilayer obj describing the sequence of layers.
130
+ alpha, delta, psi, phi = angles of the incident wave (in
131
+ radiant).
132
+ n = orders of diffractions to retain in the computation.
133
+ """
134
+ self.setMultilayer(multilayer)
135
+ self.LAMBDA = self.get_pitch()
136
+ self.alpha = alpha
137
+ self.delta = delta
138
+ self.psi = psi
139
+ self.phi = phi
140
+ self.n = n
141
+
142
+ def setMultilayer(self, m):
143
+ """Set the multilayer, simplifying it first."""
144
+ self.multilayer = m.simplify()
145
+
146
+ def get_pitch(self):
147
+ """Inspect the multilayer to check that all the binary
148
+ gratings present have the same pitch, and return it."""
149
+ idx = S.where(
150
+ [
151
+ (
152
+ isinstance(m, BinaryGrating)
153
+ | isinstance(m, SymmetricDoubleGrating)
154
+ | isinstance(m, AsymmetricDoubleGrating)
155
+ )
156
+ for m in self.multilayer
157
+ ]
158
+ )[0]
159
+ if idx.size == 0:
160
+ # warning('no BinaryGratings: better use a simple transfer matrix.')
161
+ return 1.0 # return LAMBDA: any value will do
162
+ else:
163
+ # check that all the pitches are the same!
164
+ l = S.asarray([self.multilayer[i].pitch for i in idx])
165
+ if not S.all(l == l[0]):
166
+ raise ValueError("All the BinaryGratings must have the same pitch.")
167
+ else:
168
+ return l[0]
169
+
170
+
171
+ class IsotropicRCWA(RCWA):
172
+
173
+ """Isotropic RCWA solver."""
174
+
175
+ def solve(self, wls):
176
+ """Isotropic solver.
177
+
178
+ INPUT
179
+ wls = wavelengths to scan (any asarray-able object).
180
+
181
+ OUTPUT
182
+ self.DE1, self.DE3 = power reflected and transmitted.
183
+
184
+ NOTE
185
+ see:
186
+ Moharam, "Formulation for stable and efficient implementation
187
+ of the rigorous coupled-wave analysis of binary gratings",
188
+ JOSA A, 12(5), 1995
189
+ Lalanne, "Highly improved convergence of the coupled-wave
190
+ method for TM polarization", JOSA A, 13(4), 1996
191
+ Moharam, "Stable implementation of the rigorous coupled-wave
192
+ analysis for surface-relief gratings: enhanced trasmittance
193
+ matrix approach", JOSA A, 12(5), 1995
194
+ """
195
+
196
+ self.wls = S.atleast_1d(wls)
197
+
198
+ LAMBDA = self.LAMBDA
199
+ n = self.n
200
+ multilayer = self.multilayer
201
+ alpha = self.alpha
202
+ delta = self.delta
203
+ psi = self.psi
204
+ phi = self.phi
205
+
206
+ nlayers = len(multilayer)
207
+ i = S.arange(-n, n + 1)
208
+ nood = 2 * n + 1
209
+ hmax = nood - 1
210
+
211
+ # grating vector (on the xz plane)
212
+ # grating on the xy plane
213
+ K = 2 * pi / LAMBDA * S.array([S.sin(phi), 0.0, S.cos(phi)], dtype=complex)
214
+
215
+ DE1 = S.zeros((nood, self.wls.size))
216
+ DE3 = S.zeros_like(DE1)
217
+
218
+ dirk1 = S.array(
219
+ [S.sin(alpha) * S.cos(delta), S.sin(alpha) * S.sin(delta), S.cos(alpha)]
220
+ )
221
+
222
+ # usefull matrices
223
+ I = S.eye(i.size)
224
+ I2 = S.eye(i.size * 2)
225
+ ZERO = S.zeros_like(I)
226
+
227
+ X = S.zeros((2 * nood, 2 * nood, nlayers), dtype=complex)
228
+ MTp1 = S.zeros((2 * nood, 2 * nood, nlayers), dtype=complex)
229
+ MTp2 = S.zeros_like(MTp1)
230
+
231
+ EPS2 = S.zeros(2 * hmax + 1, dtype=complex)
232
+ EPS21 = S.zeros_like(EPS2)
233
+
234
+ dlt = (i == 0).astype(int)
235
+
236
+ for iwl, wl in enumerate(self.wls):
237
+
238
+ # free space wavevector
239
+ k = 2 * pi / wl
240
+
241
+ n1 = multilayer[0].mat.n(wl).item()
242
+ n3 = multilayer[-1].mat.n(wl).item()
243
+
244
+ # incident plane wave wavevector
245
+ k1 = k * n1 * dirk1
246
+
247
+ # all the other wavevectors
248
+ tmp_x = k1[0] - i * K[0]
249
+ tmp_y = k1[1] * S.ones_like(i)
250
+ tmp_z = dispersion_relation_ordinary(tmp_x, tmp_y, k, n1)
251
+ k1i = S.r_[[tmp_x], [tmp_y], [tmp_z]]
252
+
253
+ # k2i = S.r_[[k1[0] - i*K[0]], [k1[1] - i * K[1]], [-i * K[2]]]
254
+
255
+ tmp_z = dispersion_relation_ordinary(tmp_x, tmp_y, k, n3)
256
+ k3i = S.r_[[k1i[0, :]], [k1i[1, :]], [tmp_z]]
257
+
258
+ # aliases for constant wavevectors
259
+ kx = k1i[0, :]
260
+ ky = k1[1]
261
+
262
+ # angles of reflection
263
+ # phi_i = S.arctan2(ky,kx)
264
+ phi_i = S.arctan2(ky, kx.real) # OKKIO
265
+
266
+ Kx = S.diag(kx / k)
267
+ Ky = ky / k * I
268
+ Z1 = S.diag(k1i[2, :] / (k * n1**2))
269
+ Y1 = S.diag(k1i[2, :] / k)
270
+ Z3 = S.diag(k3i[2, :] / (k * n3**2))
271
+ Y3 = S.diag(k3i[2, :] / k)
272
+ # Fc = S.diag(S.cos(phi_i))
273
+ fc = S.cos(phi_i)
274
+ # Fs = S.diag(S.sin(phi_i))
275
+ fs = S.sin(phi_i)
276
+
277
+ MR = S.asarray(
278
+ S.bmat([[I, ZERO], [-1j * Y1, ZERO], [ZERO, I], [ZERO, -1j * Z1]])
279
+ )
280
+
281
+ MT = S.asarray(
282
+ S.bmat([[I, ZERO], [1j * Y3, ZERO], [ZERO, I], [ZERO, 1j * Z3]])
283
+ )
284
+
285
+ # internal layers (grating or layer)
286
+ X.fill(0.0)
287
+ MTp1.fill(0.0)
288
+ MTp2.fill(0.0)
289
+ for nlayer in range(nlayers - 2, 0, -1): # internal layers
290
+
291
+ layer = multilayer[nlayer]
292
+ d = layer.thickness
293
+
294
+ EPS2, EPS21 = layer.getEPSFourierCoeffs(wl, n, anisotropic=False)
295
+
296
+ E = toeplitz(EPS2[hmax::-1], EPS2[hmax:])
297
+ E1 = toeplitz(EPS21[hmax::-1], EPS21[hmax:])
298
+ E11 = inv(E1)
299
+ # B = S.dot(Kx, linsolve(E,Kx)) - I
300
+ B = kx[:, S.newaxis] / k * linsolve(E, Kx) - I
301
+ # A = S.dot(Kx, Kx) - E
302
+ A = S.diag((kx / k) ** 2) - E
303
+
304
+ # Note: solution bug alfredo
305
+ # randomizzo Kx un po' a caso finche' cond(A) e' piccolo (<1e10)
306
+ # soluzione sporca... :-(
307
+ # per certi kx, l'operatore di helmholtz ha 2 autovalori nulli e A, B
308
+ # non sono invertibili --> cambio leggermente i kx... ma dovrei invece
309
+ # trattare separatamente (analiticamente) questi casi
310
+ if cond(A) > 1e10:
311
+ warning("BAD CONDITIONING: randomization of kx")
312
+ while cond(A) > 1e10:
313
+ Kx = Kx * (1 + 1e-9 * S.rand())
314
+ B = kx[:, S.newaxis] / k * linsolve(E, Kx) - I
315
+ A = S.diag((kx / k) ** 2) - E
316
+
317
+ if S.absolute(K[2] / k) > 1e-10:
318
+
319
+ raise ValueError(
320
+ "First Order Helmholtz Operator not implemented, yet!"
321
+ )
322
+
323
+ elif ky == 0 or S.allclose(S.diag(Ky / ky * k), 1):
324
+
325
+ # lalanne
326
+ # H_U_reduced = S.dot(Ky, Ky) + A
327
+ H_U_reduced = (ky / k) ** 2 * I + A
328
+ # H_S_reduced = S.dot(Ky, Ky) + S.dot(Kx, linsolve(E, S.dot(Kx, E11))) - E11
329
+ H_S_reduced = (
330
+ (ky / k) ** 2 * I
331
+ + kx[:, S.newaxis] / k * linsolve(E, kx[:, S.newaxis] / k * E11)
332
+ - E11
333
+ )
334
+
335
+ q1, W1 = eig(H_U_reduced)
336
+ q1 = S.sqrt(q1)
337
+ q2, W2 = eig(H_S_reduced)
338
+ q2 = S.sqrt(q2)
339
+
340
+ # boundary conditions
341
+
342
+ # V11 = S.dot(linsolve(A, W1), S.diag(q1))
343
+ V11 = linsolve(A, W1) * q1[S.newaxis, :]
344
+ V12 = (ky / k) * S.dot(linsolve(A, Kx), W2)
345
+ V21 = (ky / k) * S.dot(linsolve(B, Kx), linsolve(E, W1))
346
+ # V22 = S.dot(linsolve(B, W2), S.diag(q2))
347
+ V22 = linsolve(B, W2) * q2[S.newaxis, :]
348
+
349
+ # Vss = S.dot(Fc, V11)
350
+ Vss = fc[:, S.newaxis] * V11
351
+ # Wss = S.dot(Fc, W1) + S.dot(Fs, V21)
352
+ Wss = fc[:, S.newaxis] * W1 + fs[:, S.newaxis] * V21
353
+ # Vsp = S.dot(Fc, V12) - S.dot(Fs, W2)
354
+ Vsp = fc[:, S.newaxis] * V12 - fs[:, S.newaxis] * W2
355
+ # Wsp = S.dot(Fs, V22)
356
+ Wsp = fs[:, S.newaxis] * V22
357
+ # Wpp = S.dot(Fc, V22)
358
+ Wpp = fc[:, S.newaxis] * V22
359
+ # Vpp = S.dot(Fc, W2) + S.dot(Fs, V12)
360
+ Vpp = fc[:, S.newaxis] * W2 + fs[:, S.newaxis] * V12
361
+ # Wps = S.dot(Fc, V21) - S.dot(Fs, W1)
362
+ Wps = fc[:, S.newaxis] * V21 - fs[:, S.newaxis] * W1
363
+ # Vps = S.dot(Fs, V11)
364
+ Vps = fs[:, S.newaxis] * V11
365
+
366
+ Mc2bar = S.asarray(
367
+ S.bmat(
368
+ [
369
+ [Vss, Vsp, Vss, Vsp],
370
+ [Wss, Wsp, -Wss, -Wsp],
371
+ [Wps, Wpp, -Wps, -Wpp],
372
+ [Vps, Vpp, Vps, Vpp],
373
+ ]
374
+ )
375
+ )
376
+
377
+ x = S.r_[S.exp(-k * q1 * d), S.exp(-k * q2 * d)]
378
+
379
+ # Mc1 = S.dot(Mc2bar, S.diag(S.r_[S.ones_like(x), x]))
380
+ xx = S.r_[S.ones_like(x), x]
381
+ Mc1 = Mc2bar * xx[S.newaxis, :]
382
+
383
+ X[:, :, nlayer] = S.diag(x)
384
+
385
+ MTp = linsolve(Mc2bar, MT)
386
+ MTp1[:, :, nlayer] = MTp[0 : 2 * nood, :]
387
+ MTp2 = MTp[2 * nood :, :]
388
+
389
+ MT = S.dot(
390
+ Mc1,
391
+ S.r_[
392
+ I2,
393
+ S.dot(MTp2, linsolve(MTp1[:, :, nlayer], X[:, :, nlayer])),
394
+ ],
395
+ )
396
+
397
+ else:
398
+
399
+ ValueError("Second Order Helmholtz Operator not implemented, yet!")
400
+
401
+ # M = S.asarray(S.bmat([-MR, MT]))
402
+ M = S.c_[-MR, MT]
403
+ b = S.r_[
404
+ S.sin(psi) * dlt,
405
+ 1j * S.sin(psi) * n1 * S.cos(alpha) * dlt,
406
+ -1j * S.cos(psi) * n1 * dlt,
407
+ S.cos(psi) * S.cos(alpha) * dlt,
408
+ ]
409
+
410
+ x = linsolve(M, b)
411
+ R, T = S.split(x, 2)
412
+ Rs, Rp = S.split(R, 2)
413
+ for ii in range(1, nlayers - 1):
414
+ T = S.dot(linsolve(MTp1[:, :, ii], X[:, :, ii]), T)
415
+ Ts, Tp = S.split(T, 2)
416
+
417
+ DE1[:, iwl] = (k1i[2, :] / (k1[2])).real * S.absolute(Rs) ** 2 + (
418
+ k1i[2, :] / (k1[2] * n1**2)
419
+ ).real * S.absolute(Rp) ** 2
420
+ DE3[:, iwl] = (k3i[2, :] / (k1[2])).real * S.absolute(Ts) ** 2 + (
421
+ k3i[2, :] / (k1[2] * n3**2)
422
+ ).real * S.absolute(Tp) ** 2
423
+
424
+ # save the results
425
+ self.DE1 = DE1
426
+ self.DE3 = DE3
427
+
428
+ return self
429
+
430
+ # def plot(self):
431
+ # """Plot the diffraction efficiencies."""
432
+ # g = Gnuplot.Gnuplot()
433
+ # g('set xlabel "$\lambda$"')
434
+ # g('set ylabel "diffraction efficiency"')
435
+ # g('set yrange [0:1]')
436
+ # g('set data style linespoints')
437
+ # g.plot(Gnuplot.Data(self.wls, self.DE1[self.n,:], with_ = 'linespoints', title = 'DE1'), \
438
+ # Gnuplot.Data(self.wls, self.DE3[self.n,:], with_ = 'linespoints', title = 'DE3'))
439
+ # raw_input('press enter to close the graph...')
440
+
441
+ def __str__(self):
442
+ return (
443
+ "ISOTROPIC RCWA SOLVER\n\n%s\n\nLAMBDA = %g\nalpha = %g\ndelta = %g\npsi = %g\nphi = %g\nn = %d"
444
+ % (
445
+ self.multilayer.__str__(),
446
+ self.LAMBDA,
447
+ self.alpha,
448
+ self.delta,
449
+ self.psi,
450
+ self.phi,
451
+ self.n,
452
+ )
453
+ )
454
+
455
+
456
+ class AnisotropicRCWA(RCWA):
457
+
458
+ """Anisotropic RCWA solver."""
459
+
460
+ def solve(self, wls):
461
+ """Anisotropic solver.
462
+
463
+ INPUT
464
+ wls = wavelengths to scan (any asarray-able object).
465
+
466
+ OUTPUT
467
+ self.DEO1, self.DEE1, self.DEO3, self.DEE3 = power reflected
468
+ and transmitted.
469
+ """
470
+
471
+ self.wls = S.atleast_1d(wls)
472
+
473
+ LAMBDA = self.LAMBDA
474
+ n = self.n
475
+ multilayer = self.multilayer
476
+ alpha = self.alpha
477
+ delta = self.delta
478
+ psi = self.psi
479
+ phi = self.phi
480
+
481
+ nlayers = len(multilayer)
482
+ i = S.arange(-n, n + 1)
483
+ nood = 2 * n + 1
484
+ hmax = nood - 1
485
+
486
+ DEO1 = S.zeros((nood, self.wls.size))
487
+ DEO3 = S.zeros_like(DEO1)
488
+ DEE1 = S.zeros_like(DEO1)
489
+ DEE3 = S.zeros_like(DEO1)
490
+
491
+ c1 = S.array([1.0, 0.0, 0.0])
492
+ c3 = S.array([1.0, 0.0, 0.0])
493
+ # grating on the xy plane
494
+ K = 2 * pi / LAMBDA * S.array([S.sin(phi), 0.0, S.cos(phi)], dtype=complex)
495
+ dirk1 = S.array(
496
+ [S.sin(alpha) * S.cos(delta), S.sin(alpha) * S.sin(delta), S.cos(alpha)]
497
+ )
498
+
499
+ # D polarization vector
500
+ u = S.array(
501
+ [
502
+ S.cos(psi) * S.cos(alpha) * S.cos(delta) - S.sin(psi) * S.sin(delta),
503
+ S.cos(psi) * S.cos(alpha) * S.sin(delta) + S.sin(psi) * S.cos(delta),
504
+ -S.cos(psi) * S.sin(alpha),
505
+ ]
506
+ )
507
+
508
+ kO1i = S.zeros((3, i.size), dtype=complex)
509
+ kE1i = S.zeros_like(kO1i)
510
+ kO3i = S.zeros_like(kO1i)
511
+ kE3i = S.zeros_like(kO1i)
512
+
513
+ Mp = S.zeros((4 * nood, 4 * nood, nlayers), dtype=complex)
514
+ M = S.zeros((4 * nood, 4 * nood, nlayers), dtype=complex)
515
+
516
+ dlt = (i == 0).astype(int)
517
+
518
+ for iwl, wl in enumerate(self.wls):
519
+
520
+ nO1 = nE1 = multilayer[0].mat.n(wl).item()
521
+ nO3 = nE3 = multilayer[-1].mat.n(wl).item()
522
+
523
+ # wavevectors
524
+ k = 2 * pi / wl
525
+
526
+ eps1 = S.diag(S.asarray([nE1, nO1, nO1]) ** 2)
527
+ eps3 = S.diag(S.asarray([nE3, nO3, nO3]) ** 2)
528
+
529
+ # ordinary wave
530
+ abskO1 = k * nO1
531
+ # abskO3 = k * nO3
532
+ # extraordinary wave
533
+ # abskE1 = k * nO1 *nE1 / S.sqrt(nO1**2 + (nE1**2 - nO1**2) * S.dot(-c1, dirk1)**2)
534
+ # abskE3 = k * nO3 *nE3 / S.sqrt(nO3**2 + (nE3**2 - nO3**2) * S.dot(-c3, dirk1)**2)
535
+
536
+ k1 = abskO1 * dirk1
537
+
538
+ kO1i[0, :] = k1[0] - i * K[0]
539
+ kO1i[1, :] = k1[1] * S.ones_like(i)
540
+ kO1i[2, :] = -dispersion_relation_ordinary(kO1i[0, :], kO1i[1, :], k, nO1)
541
+
542
+ kE1i[0, :] = kO1i[0, :]
543
+ kE1i[1, :] = kO1i[1, :]
544
+ kE1i[2, :] = -dispersion_relation_extraordinary(
545
+ kE1i[0, :], kE1i[1, :], k, nO1, nE1, c1
546
+ )
547
+
548
+ kO3i[0, :] = kO1i[0, :]
549
+ kO3i[1, :] = kO1i[1, :]
550
+ kO3i[2, :] = dispersion_relation_ordinary(kO3i[0, :], kO3i[1, :], k, nO3)
551
+
552
+ kE3i[0, :] = kO1i[0, :]
553
+ kE3i[1, :] = kO1i[1, :]
554
+ kE3i[2, :] = dispersion_relation_extraordinary(
555
+ kE3i[0, :], kE3i[1, :], k, nO3, nE3, c3
556
+ )
557
+
558
+ # k2i = S.r_[[k1[0] - i * K[0]], [k1[1] - i * K[1]], [k1[2] - i * K[2]]]
559
+ k2i = S.r_[[k1[0] - i * K[0]], [k1[1] - i * K[1]], [-i * K[2]]]
560
+
561
+ # aliases for constant wavevectors
562
+ kx = kO1i[0, :] # o kE1i(1,;), tanto e' lo stesso
563
+ ky = k1[1]
564
+
565
+ # matrices
566
+ I = S.eye(nood, dtype=complex)
567
+ ZERO = S.zeros((nood, nood), dtype=complex)
568
+ Kx = S.diag(kx / k)
569
+ Ky = ky / k * I
570
+ Kz = S.diag(k2i[2, :] / k)
571
+ KO1z = S.diag(kO1i[2, :] / k)
572
+ KE1z = S.diag(kE1i[2, :] / k)
573
+ KO3z = S.diag(kO3i[2, :] / k)
574
+ KE3z = S.diag(kE3i[2, :] / k)
575
+
576
+ ARO = Kx * eps1[0, 0] + Ky * eps1[1, 0] + KO1z * eps1[2, 0]
577
+ BRO = Kx * eps1[0, 1] + Ky * eps1[1, 1] + KO1z * eps1[2, 1]
578
+ CRO_1 = inv(Kx * eps1[0, 2] + Ky * eps1[1, 2] + KO1z * eps1[2, 2])
579
+
580
+ ARE = Kx * eps1[0, 0] + Ky * eps1[1, 0] + KE1z * eps1[2, 0]
581
+ BRE = Kx * eps1[0, 1] + Ky * eps1[1, 1] + KE1z * eps1[2, 1]
582
+ CRE_1 = inv(Kx * eps1[0, 2] + Ky * eps1[1, 2] + KE1z * eps1[2, 2])
583
+
584
+ ATO = Kx * eps3[0, 0] + Ky * eps3[1, 0] + KO3z * eps3[2, 0]
585
+ BTO = Kx * eps3[0, 1] + Ky * eps3[1, 1] + KO3z * eps3[2, 1]
586
+ CTO_1 = inv(Kx * eps3[0, 2] + Ky * eps3[1, 2] + KO3z * eps3[2, 2])
587
+
588
+ ATE = Kx * eps3[0, 0] + Ky * eps3[1, 0] + KE3z * eps3[2, 0]
589
+ BTE = Kx * eps3[0, 1] + Ky * eps3[1, 1] + KE3z * eps3[2, 1]
590
+ CTE_1 = inv(Kx * eps3[0, 2] + Ky * eps3[1, 2] + KE3z * eps3[2, 2])
591
+
592
+ DRE = c1[1] * KE1z - c1[2] * Ky
593
+ ERE = c1[2] * Kx - c1[0] * KE1z
594
+ FRE = c1[0] * Ky - c1[1] * Kx
595
+
596
+ DTE = c3[1] * KE3z - c3[2] * Ky
597
+ ETE = c3[2] * Kx - c3[0] * KE3z
598
+ FTE = c3[0] * Ky - c3[1] * Kx
599
+
600
+ b = S.r_[
601
+ u[0] * dlt,
602
+ u[1] * dlt,
603
+ (k1[1] / k * u[2] - k1[2] / k * u[1]) * dlt,
604
+ (k1[2] / k * u[0] - k1[0] / k * u[2]) * dlt,
605
+ ]
606
+ Ky_CRO_1 = ky / k * CRO_1
607
+ Ky_CRE_1 = ky / k * CRE_1
608
+ Kx_CRO_1 = kx[:, S.newaxis] / k * CRO_1
609
+ Kx_CRE_1 = kx[:, S.newaxis] / k * CRE_1
610
+ MR31 = -S.dot(Ky_CRO_1, ARO)
611
+ MR32 = -S.dot(Ky_CRO_1, BRO) - KO1z
612
+ MR33 = -S.dot(Ky_CRE_1, ARE)
613
+ MR34 = -S.dot(Ky_CRE_1, BRE) - KE1z
614
+ MR41 = S.dot(Kx_CRO_1, ARO) + KO1z
615
+ MR42 = S.dot(Kx_CRO_1, BRO)
616
+ MR43 = S.dot(Kx_CRE_1, ARE) + KE1z
617
+ MR44 = S.dot(Kx_CRE_1, BRE)
618
+ MR = S.asarray(
619
+ S.bmat(
620
+ [
621
+ [I, ZERO, I, ZERO],
622
+ [ZERO, I, ZERO, I],
623
+ [MR31, MR32, MR33, MR34],
624
+ [MR41, MR42, MR43, MR44],
625
+ ]
626
+ )
627
+ )
628
+
629
+ Ky_CTO_1 = ky / k * CTO_1
630
+ Ky_CTE_1 = ky / k * CTE_1
631
+ Kx_CTO_1 = kx[:, S.newaxis] / k * CTO_1
632
+ Kx_CTE_1 = kx[:, S.newaxis] / k * CTE_1
633
+ MT31 = -S.dot(Ky_CTO_1, ATO)
634
+ MT32 = -S.dot(Ky_CTO_1, BTO) - KO3z
635
+ MT33 = -S.dot(Ky_CTE_1, ATE)
636
+ MT34 = -S.dot(Ky_CTE_1, BTE) - KE3z
637
+ MT41 = S.dot(Kx_CTO_1, ATO) + KO3z
638
+ MT42 = S.dot(Kx_CTO_1, BTO)
639
+ MT43 = S.dot(Kx_CTE_1, ATE) + KE3z
640
+ MT44 = S.dot(Kx_CTE_1, BTE)
641
+ MT = S.asarray(
642
+ S.bmat(
643
+ [
644
+ [I, ZERO, I, ZERO],
645
+ [ZERO, I, ZERO, I],
646
+ [MT31, MT32, MT33, MT34],
647
+ [MT41, MT42, MT43, MT44],
648
+ ]
649
+ )
650
+ )
651
+
652
+ Mp.fill(0.0)
653
+ M.fill(0.0)
654
+
655
+ for nlayer in range(nlayers - 2, 0, -1): # internal layers
656
+
657
+ layer = multilayer[nlayer]
658
+ thickness = layer.thickness
659
+
660
+ EPS2, EPS21 = layer.getEPSFourierCoeffs(wl, n, anisotropic=True)
661
+
662
+ # Exx = S.squeeze(EPS2[0, 0, :])
663
+ # Exx = toeplitz(S.flipud(Exx[0:hmax + 1]), Exx[hmax:])
664
+ Exy = S.squeeze(EPS2[0, 1, :])
665
+ Exy = toeplitz(S.flipud(Exy[0 : hmax + 1]), Exy[hmax:])
666
+ Exz = S.squeeze(EPS2[0, 2, :])
667
+ Exz = toeplitz(S.flipud(Exz[0 : hmax + 1]), Exz[hmax:])
668
+
669
+ Eyx = S.squeeze(EPS2[1, 0, :])
670
+ Eyx = toeplitz(S.flipud(Eyx[0 : hmax + 1]), Eyx[hmax:])
671
+ Eyy = S.squeeze(EPS2[1, 1, :])
672
+ Eyy = toeplitz(S.flipud(Eyy[0 : hmax + 1]), Eyy[hmax:])
673
+ Eyz = S.squeeze(EPS2[1, 2, :])
674
+ Eyz = toeplitz(S.flipud(Eyz[0 : hmax + 1]), Eyz[hmax:])
675
+
676
+ Ezx = S.squeeze(EPS2[2, 0, :])
677
+ Ezx = toeplitz(S.flipud(Ezx[0 : hmax + 1]), Ezx[hmax:])
678
+ Ezy = S.squeeze(EPS2[2, 1, :])
679
+ Ezy = toeplitz(S.flipud(Ezy[0 : hmax + 1]), Ezy[hmax:])
680
+ Ezz = S.squeeze(EPS2[2, 2, :])
681
+ Ezz = toeplitz(S.flipud(Ezz[0 : hmax + 1]), Ezz[hmax:])
682
+
683
+ Exx_1 = S.squeeze(EPS21[0, 0, :])
684
+ Exx_1 = toeplitz(S.flipud(Exx_1[0 : hmax + 1]), Exx_1[hmax:])
685
+ Exx_1_1 = inv(Exx_1)
686
+
687
+ # lalanne
688
+ Ezz_1 = inv(Ezz)
689
+ Ky_Ezz_1 = ky / k * Ezz_1
690
+ Kx_Ezz_1 = kx[:, S.newaxis] / k * Ezz_1
691
+ Exz_Ezz_1 = S.dot(Exz, Ezz_1)
692
+ Eyz_Ezz_1 = S.dot(Eyz, Ezz_1)
693
+ H11 = 1j * S.dot(Ky_Ezz_1, Ezy)
694
+ H12 = 1j * S.dot(Ky_Ezz_1, Ezx)
695
+ H13 = S.dot(Ky_Ezz_1, Kx)
696
+ H14 = I - S.dot(Ky_Ezz_1, Ky)
697
+ H21 = 1j * S.dot(Kx_Ezz_1, Ezy)
698
+ H22 = 1j * S.dot(Kx_Ezz_1, Ezx)
699
+ H23 = S.dot(Kx_Ezz_1, Kx) - I
700
+ H24 = -S.dot(Kx_Ezz_1, Ky)
701
+ H31 = S.dot(Kx, Ky) + Exy - S.dot(Exz_Ezz_1, Ezy)
702
+ H32 = Exx_1_1 - S.dot(Ky, Ky) - S.dot(Exz_Ezz_1, Ezx)
703
+ H33 = 1j * S.dot(Exz_Ezz_1, Kx)
704
+ H34 = -1j * S.dot(Exz_Ezz_1, Ky)
705
+ H41 = S.dot(Kx, Kx) - Eyy + S.dot(Eyz_Ezz_1, Ezy)
706
+ H42 = -S.dot(Kx, Ky) - Eyx + S.dot(Eyz_Ezz_1, Ezx)
707
+ H43 = -1j * S.dot(Eyz_Ezz_1, Kx)
708
+ H44 = 1j * S.dot(Eyz_Ezz_1, Ky)
709
+ H = 1j * S.diag(S.repeat(S.diag(Kz), 4)) + S.asarray(
710
+ S.bmat(
711
+ [
712
+ [H11, H12, H13, H14],
713
+ [H21, H22, H23, H24],
714
+ [H31, H32, H33, H34],
715
+ [H41, H42, H43, H44],
716
+ ]
717
+ )
718
+ )
719
+
720
+ q, W = eig(H)
721
+ W1, W2, W3, W4 = S.split(W, 4)
722
+
723
+ #
724
+ # boundary conditions
725
+ #
726
+ # x = [R T]
727
+ # R = [ROx ROy REx REy]
728
+ # T = [TOx TOy TEx TEy]
729
+ # b + MR.R = M1p.c
730
+ # M1.c = M2p.c
731
+ # ...
732
+ # ML.c = MT.T
733
+ # therefore: b + MR.R = (M1p.M1^-1.M2p.M2^-1. ...).MT.T
734
+ # missing equations from (46)..(49) in glytsis_rigorous
735
+ # [b] = [-MR Mtot.MT] [R]
736
+ # [0] [...........] [T]
737
+
738
+ z = S.zeros_like(q)
739
+ z[S.where(q.real > 0)] = -thickness
740
+ D = S.exp(k * q * z)
741
+ Sy0 = W1 * D[S.newaxis, :]
742
+ Sx0 = W2 * D[S.newaxis, :]
743
+ Uy0 = W3 * D[S.newaxis, :]
744
+ Ux0 = W4 * D[S.newaxis, :]
745
+
746
+ z = thickness * S.ones_like(q)
747
+ z[S.where(q.real > 0)] = 0
748
+ D = S.exp(k * q * z)
749
+ D1 = S.exp(-1j * k2i[2, :] * thickness)
750
+ Syd = D1[:, S.newaxis] * W1 * D[S.newaxis, :]
751
+ Sxd = D1[:, S.newaxis] * W2 * D[S.newaxis, :]
752
+ Uyd = D1[:, S.newaxis] * W3 * D[S.newaxis, :]
753
+ Uxd = D1[:, S.newaxis] * W4 * D[S.newaxis, :]
754
+
755
+ Mp[:, :, nlayer] = S.r_[Sx0, Sy0, -1j * Ux0, -1j * Uy0]
756
+ M[:, :, nlayer] = S.r_[Sxd, Syd, -1j * Uxd, -1j * Uyd]
757
+
758
+ Mtot = S.eye(4 * nood, dtype=complex)
759
+ for nlayer in range(1, nlayers - 1):
760
+ Mtot = S.dot(S.dot(Mtot, Mp[:, :, nlayer]), inv(M[:, :, nlayer]))
761
+
762
+ BC_b = S.r_[b, S.zeros_like(b)]
763
+ BC_A1 = S.c_[-MR, S.dot(Mtot, MT)]
764
+ BC_A2 = S.asarray(
765
+ S.bmat(
766
+ [
767
+ [
768
+ (c1[0] * I - c1[2] * S.dot(CRO_1, ARO)),
769
+ (c1[1] * I - c1[2] * S.dot(CRO_1, BRO)),
770
+ ZERO,
771
+ ZERO,
772
+ ZERO,
773
+ ZERO,
774
+ ZERO,
775
+ ZERO,
776
+ ],
777
+ [
778
+ ZERO,
779
+ ZERO,
780
+ (DRE - S.dot(S.dot(FRE, CRE_1), ARE)),
781
+ (ERE - S.dot(S.dot(FRE, CRE_1), BRE)),
782
+ ZERO,
783
+ ZERO,
784
+ ZERO,
785
+ ZERO,
786
+ ],
787
+ [
788
+ ZERO,
789
+ ZERO,
790
+ ZERO,
791
+ ZERO,
792
+ (c3[0] * I - c3[2] * S.dot(CTO_1, ATO)),
793
+ (c3[1] * I - c3[2] * S.dot(CTO_1, BTO)),
794
+ ZERO,
795
+ ZERO,
796
+ ],
797
+ [
798
+ ZERO,
799
+ ZERO,
800
+ ZERO,
801
+ ZERO,
802
+ ZERO,
803
+ ZERO,
804
+ (DTE - S.dot(S.dot(FTE, CTE_1), ATE)),
805
+ (ETE - S.dot(S.dot(FTE, CTE_1), BTE)),
806
+ ],
807
+ ]
808
+ )
809
+ )
810
+
811
+ BC_A = S.r_[BC_A1, BC_A2]
812
+
813
+ x = linsolve(BC_A, BC_b)
814
+
815
+ ROx, ROy, REx, REy, TOx, TOy, TEx, TEy = S.split(x, 8)
816
+
817
+ ROz = -S.dot(CRO_1, (S.dot(ARO, ROx) + S.dot(BRO, ROy)))
818
+ REz = -S.dot(CRE_1, (S.dot(ARE, REx) + S.dot(BRE, REy)))
819
+ TOz = -S.dot(CTO_1, (S.dot(ATO, TOx) + S.dot(BTO, TOy)))
820
+ TEz = -S.dot(CTE_1, (S.dot(ATE, TEx) + S.dot(BTE, TEy)))
821
+
822
+ denom = (k1[2] - S.dot(u, k1) * u[2]).real
823
+ DEO1[:, iwl] = (
824
+ -(
825
+ (S.absolute(ROx) ** 2 + S.absolute(ROy) ** 2 + S.absolute(ROz) ** 2)
826
+ * S.conj(kO1i[2, :])
827
+ - (ROx * kO1i[0, :] + ROy * kO1i[1, :] + ROz * kO1i[2, :])
828
+ * S.conj(ROz)
829
+ ).real
830
+ / denom
831
+ )
832
+ DEE1[:, iwl] = (
833
+ -(
834
+ (S.absolute(REx) ** 2 + S.absolute(REy) ** 2 + S.absolute(REz) ** 2)
835
+ * S.conj(kE1i[2, :])
836
+ - (REx * kE1i[0, :] + REy * kE1i[1, :] + REz * kE1i[2, :])
837
+ * S.conj(REz)
838
+ ).real
839
+ / denom
840
+ )
841
+ DEO3[:, iwl] = (
842
+ (S.absolute(TOx) ** 2 + S.absolute(TOy) ** 2 + S.absolute(TOz) ** 2)
843
+ * S.conj(kO3i[2, :])
844
+ - (TOx * kO3i[0, :] + TOy * kO3i[1, :] + TOz * kO3i[2, :]) * S.conj(TOz)
845
+ ).real / denom
846
+ DEE3[:, iwl] = (
847
+ (S.absolute(TEx) ** 2 + S.absolute(TEy) ** 2 + S.absolute(TEz) ** 2)
848
+ * S.conj(kE3i[2, :])
849
+ - (TEx * kE3i[0, :] + TEy * kE3i[1, :] + TEz * kE3i[2, :]) * S.conj(TEz)
850
+ ).real / denom
851
+
852
+ # save the results
853
+ self.DEO1 = DEO1
854
+ self.DEE1 = DEE1
855
+ self.DEO3 = DEO3
856
+ self.DEE3 = DEE3
857
+
858
+ return self
859
+
860
+ # def plot(self):
861
+ # """Plot the diffraction efficiencies."""
862
+ # g = Gnuplot.Gnuplot()
863
+ # g('set xlabel "$\lambda$"')
864
+ # g('set ylabel "diffraction efficiency"')
865
+ # g('set yrange [0:1]')
866
+ # g('set data style linespoints')
867
+ # g.plot(Gnuplot.Data(self.wls, self.DEO1[self.n,:], with_ = 'linespoints', title = 'DEO1'), \
868
+ # Gnuplot.Data(self.wls, self.DEO3[self.n,:], with_ = 'linespoints', title = 'DEO3'), \
869
+ # Gnuplot.Data(self.wls, self.DEE1[self.n,:], with_ = 'linespoints', title = 'DEE1'), \
870
+ # Gnuplot.Data(self.wls, self.DEE3[self.n,:], with_ = 'linespoints', title = 'DEE3'))
871
+ # raw_input('press enter to close the graph...')
872
+
873
+ def __str__(self):
874
+ return (
875
+ "ANISOTROPIC RCWA SOLVER\n\n%s\n\nLAMBDA = %g\nalpha = %g\ndelta = %g\npsi = %g\nphi = %g\nn = %d"
876
+ % (
877
+ self.multilayer.__str__(),
878
+ self.LAMBDA,
879
+ self.alpha,
880
+ self.delta,
881
+ self.psi,
882
+ self.phi,
883
+ self.n,
884
+ )
885
+ )