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,2422 @@
1
+ """Film Mode Matching Mode Solver
2
+
3
+ Implementation of the Film Mode Matching (FMM) algorithm, as described in:
4
+
5
+ - Sudbo, "Film mode matching a versatile numerical method for vector mode field calculations in dielectric waveguides",
6
+ Pure App. Optics, 2 (1993), 211-233
7
+ - Sudbo, "Improved formulation of the film mode matching method for mode field calculations in dielectric waveguides",
8
+ Pure App. Optics, 3 (1994), 381-388
9
+
10
+ Examples
11
+ ========
12
+
13
+ See L{FMM1d} and L{FMM2d}.
14
+
15
+ """
16
+ import copy
17
+ from functools import reduce
18
+
19
+ __author__ = "Luca Gamberale & Lorenzo Bolla"
20
+
21
+ import numpy
22
+ import scipy
23
+ import scipy.optimize
24
+ import pylab
25
+
26
+ import EMpy.utils
27
+ from EMpy.modesolvers.interface import Mode, ModeSolver
28
+
29
+
30
+ class Message:
31
+ def __init__(self, msg, verbosity=0):
32
+ self.msg = msg
33
+ self.verbosity = verbosity
34
+
35
+ def show(self, verbosity=0):
36
+ if self.verbosity <= verbosity:
37
+ print((self.verbosity - 1) * "\t" + self.msg)
38
+
39
+
40
+ class Struct:
41
+ """Empty class to fill with whatever I want. Maybe a dictionary would do?"""
42
+
43
+ pass
44
+
45
+
46
+ class Boundary:
47
+ """Boundary conditions.
48
+
49
+ Electric and Magnetic boundary conditions are translated to Symmetric
50
+ and Antisymmetric for each field.
51
+
52
+ @ivar xleft: Left bc on x.
53
+ @ivar xright: Right bc on x.
54
+ @ivar yleft: Left bc on y.
55
+ @ivar yright: Right bc on y.
56
+
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ xleft="Electric Wall",
62
+ yleft="Magnetic Wall",
63
+ xright="Electric Wall",
64
+ yright="Magnetic Wall",
65
+ ):
66
+ """Set the boundary conditions, validate and translate."""
67
+ self.xleft = xleft
68
+ self.yleft = yleft
69
+ self.xright = xright
70
+ self.yright = yright
71
+ self.validate()
72
+ self.translate()
73
+
74
+ def validate(self):
75
+ """Validate the input.
76
+
77
+ @raise ValueError: Unknown boundary.
78
+
79
+ """
80
+
81
+ if not reduce(
82
+ lambda x, y: x & y,
83
+ [
84
+ (x == "Electric Wall") | (x == "Magnetic Wall")
85
+ for x in [self.xleft, self.yleft, self.xright, self.yright]
86
+ ],
87
+ ):
88
+ raise ValueError("Unknown boundary.")
89
+
90
+ def translate(self):
91
+ """Translate for each field.
92
+
93
+ @raise ValueError: Unknown boundary.
94
+
95
+ """
96
+
97
+ self.xh = ""
98
+ self.xe = ""
99
+ self.yh = ""
100
+ self.ye = ""
101
+ if self.xleft == "Electric Wall":
102
+ self.xh += "A"
103
+ self.xe += "S"
104
+ elif self.xleft == "Magnetic Wall":
105
+ self.xh += "S"
106
+ self.xe += "A"
107
+ else:
108
+ raise ValueError("Unknown boundary.")
109
+ if self.xright == "Electric Wall":
110
+ self.xh += "A"
111
+ self.xe += "S"
112
+ elif self.xright == "Magnetic Wall":
113
+ self.xh += "S"
114
+ self.xe += "A"
115
+ else:
116
+ raise ValueError("Unknown boundary.")
117
+ if self.yleft == "Electric Wall":
118
+ self.yh += "A"
119
+ self.ye += "S"
120
+ elif self.yleft == "Magnetic Wall":
121
+ self.yh += "S"
122
+ self.ye += "A"
123
+ else:
124
+ raise ValueError("Unknown boundary.")
125
+ if self.yright == "Electric Wall":
126
+ self.yh += "A"
127
+ self.ye += "S"
128
+ elif self.yright == "Magnetic Wall":
129
+ self.yh += "S"
130
+ self.ye += "A"
131
+ else:
132
+ raise ValueError("Unknown boundary.")
133
+
134
+ def __str__(self):
135
+ return "xleft = %s, xright = %s, yleft = %s, yright = %s" % (
136
+ self.xleft,
137
+ self.xright,
138
+ self.yleft,
139
+ self.yright,
140
+ )
141
+
142
+
143
+ class Slice:
144
+ """One dimensional arrangement of layers and 1d modes.
145
+
146
+ A slice is made of a stack of layers, i.e. refractive indeces with a thickness,
147
+ with given boundary conditions.
148
+ It holds 1d modes, both TE and TM.
149
+
150
+ @ivar x1: start point of the slice in x.
151
+ @ivar x2: end point of the slice in x.
152
+ @ivar Uy: array of points delimiting the layers.
153
+ @ivar boundary: boundary conditions.
154
+ @ivar modie: E modes.
155
+ @ivar modih: H modes.
156
+ @ivar Ux: array of points delimiting the slices in x (internally set).
157
+ @ivar refractiveindex: refractive index of all the slices (internally set).
158
+ @ivar epsilon: epsilon of all the slices (internally set).
159
+ @ivar wl: vacuum wavelength.
160
+
161
+ """
162
+
163
+ def __init__(self, x1, x2, Uy, boundary, modie, modih):
164
+ self.x1 = x1
165
+ self.x2 = x2
166
+ self.Uy = Uy
167
+ self.boundary = boundary
168
+ self.modie = modie
169
+ self.modih = modih
170
+
171
+ def __str__(self):
172
+ return "x1 = %g, x2 = %g\nUy = %s\nboundary = %s" % (
173
+ self.x1,
174
+ self.x2,
175
+ self.Uy,
176
+ self.boundary,
177
+ )
178
+
179
+
180
+ class FMMMode1d(Mode):
181
+ """One dimensional mode.
182
+
183
+ Note
184
+ ====
185
+
186
+ Virtual class.
187
+
188
+ """
189
+
190
+ pass
191
+
192
+
193
+ class FMMMode1dx(FMMMode1d):
194
+ """Matching coefficients in the x-direction.
195
+
196
+ L{FMMMode1dy}s are weighted by these coefficients to assure continuity.
197
+
198
+ """
199
+
200
+ def __str__(self):
201
+ return "sl = %s\nsr = %s\nal = %s\nar = %s\nk = %s\nU = %s" % (
202
+ self.sl.__str__(),
203
+ self.sr.__str__(),
204
+ self.al.__str__(),
205
+ self.ar.__str__(),
206
+ self.k.__str__(),
207
+ self.U.__str__(),
208
+ )
209
+
210
+
211
+ class FMMMode1dy(FMMMode1d):
212
+ """One dimensional mode.
213
+
214
+ It holds the coefficients that describe the mode in the FMM expansion.
215
+
216
+ Note
217
+ ====
218
+
219
+ The mode is suppose one dimensional, in the y direction.
220
+
221
+ @ivar sl: array of value of the mode at the lhs of each slice.
222
+ @ivar sr: array of value of the mode at the rhs of each slice.
223
+ @ivar al: array of value of the derivative of the mode at the lhs of each slice.
224
+ @ivar ar: array of value of the derivative of the mode at the lhs of each slice.
225
+ @ivar k: wavevector inside each layer.
226
+ @ivar keff: effective wavevector.
227
+ @ivar zero: how good the mode is? it must be as close to zero as possible!
228
+ @ivar Uy: array of points delimiting the layers.
229
+
230
+ """
231
+
232
+ def eval(self, y_):
233
+ """Evaluate the mode at y."""
234
+ y = numpy.atleast_1d(y_)
235
+ ny = len(y)
236
+ f = numpy.zeros(ny, dtype=complex)
237
+ for iU in range(len(self.U) - 1):
238
+ k = self.k[iU]
239
+ sl = self.sl[iU]
240
+ al = self.al[iU]
241
+ Ul = self.U[iU]
242
+ Ur = self.U[iU + 1]
243
+ idx = numpy.where((Ul <= y) & (y <= Ur))
244
+ yy = y[idx] - Ul
245
+ f[idx] = sl * numpy.cos(k * yy) + al * sinxsux(k * yy) * yy
246
+ return f
247
+
248
+ def plot(self, y):
249
+ f = self.eval(y)
250
+ pylab.plot(y, numpy.real(f), y, numpy.imag(y))
251
+ pylab.legend(("real", "imag"))
252
+ pylab.xlabel("y")
253
+ pylab.ylabel("mode1d")
254
+ pylab.show()
255
+
256
+ def __str__(self):
257
+ return (
258
+ "sl = %s\nsr = %s\nal = %s\nar = %s\nk = %s\nkeff = %s\nzero = %s\nU = %s"
259
+ % (
260
+ self.sl.__str__(),
261
+ self.sr.__str__(),
262
+ self.al.__str__(),
263
+ self.ar.__str__(),
264
+ self.k.__str__(),
265
+ self.keff.__str__(),
266
+ self.zero.__str__(),
267
+ self.U.__str__(),
268
+ )
269
+ )
270
+
271
+
272
+ class FMMMode2d(Mode):
273
+ """Two dimensional mode.
274
+
275
+ It holds the coefficients that describe the mode in the FMM expansion.
276
+
277
+ """
278
+
279
+ def get_x(self, n=100):
280
+ return numpy.linspace(self.slicesx[0].Ux[0], self.slicesx[0].Ux[-1], n)
281
+
282
+ def get_y(self, n=100):
283
+ return numpy.linspace(self.slicesx[0].Uy[0], self.slicesx[0].Uy[-1], n)
284
+
285
+ def eval(self, x_=None, y_=None):
286
+ """Evaluate the mode at x,y."""
287
+
288
+ if x_ is None:
289
+ x = self.get_x()
290
+ else:
291
+ x = numpy.atleast_1d(x_)
292
+ if y_ is None:
293
+ y = self.get_y()
294
+ else:
295
+ y = numpy.atleast_1d(y_)
296
+
297
+ nmodi = len(self.modie)
298
+ lenx = len(x)
299
+ leny = len(y)
300
+ k0 = 2.0 * numpy.pi / self.slicesx[0].wl
301
+ kz = self.keff
302
+
303
+ uh = numpy.zeros((nmodi, lenx), dtype=complex)
304
+ ue = numpy.zeros_like(uh)
305
+ udoth = numpy.zeros_like(uh)
306
+ udote = numpy.zeros_like(uh)
307
+ Exsh = numpy.zeros((leny, nmodi), dtype=complex)
308
+ Exah = numpy.zeros_like(Exsh)
309
+ Exse = numpy.zeros_like(Exsh)
310
+ Exae = numpy.zeros_like(Exsh)
311
+ Eysh = numpy.zeros_like(Exsh)
312
+ Eyah = numpy.zeros_like(Exsh)
313
+ Eyse = numpy.zeros_like(Exsh)
314
+ Eyae = numpy.zeros_like(Exsh)
315
+ Ezsh = numpy.zeros_like(Exsh)
316
+ Ezah = numpy.zeros_like(Exsh)
317
+ Ezse = numpy.zeros_like(Exsh)
318
+ Ezae = numpy.zeros_like(Exsh)
319
+ cBxsh = numpy.zeros_like(Exsh)
320
+ cBxah = numpy.zeros_like(Exsh)
321
+ cBxse = numpy.zeros_like(Exsh)
322
+ cBxae = numpy.zeros_like(Exsh)
323
+ cBysh = numpy.zeros_like(Exsh)
324
+ cByah = numpy.zeros_like(Exsh)
325
+ cByse = numpy.zeros_like(Exsh)
326
+ cByae = numpy.zeros_like(Exsh)
327
+ cBzsh = numpy.zeros_like(Exsh)
328
+ cBzah = numpy.zeros_like(Exsh)
329
+ cBzse = numpy.zeros_like(Exsh)
330
+ cBzae = numpy.zeros_like(Exsh)
331
+ ExTE = numpy.zeros((leny, lenx), dtype=complex)
332
+ EyTE = numpy.zeros_like(ExTE)
333
+ EzTE = numpy.zeros_like(ExTE)
334
+ ExTM = numpy.zeros_like(ExTE)
335
+ EyTM = numpy.zeros_like(ExTE)
336
+ EzTM = numpy.zeros_like(ExTE)
337
+ cBxTE = numpy.zeros_like(ExTE)
338
+ cByTE = numpy.zeros_like(ExTE)
339
+ cBzTE = numpy.zeros_like(ExTE)
340
+ cBxTM = numpy.zeros_like(ExTE)
341
+ cByTM = numpy.zeros_like(ExTE)
342
+ cBzTM = numpy.zeros_like(ExTE)
343
+
344
+ for mx, slice in enumerate(self.slicesx):
345
+
346
+ idx = numpy.where((slice.x1 <= x) & (x < slice.x2))
347
+ x2 = x[idx] - slice.x1
348
+ x1 = slice.x2 - x[idx]
349
+ dx = slice.x2 - slice.x1
350
+
351
+ for n in range(nmodi):
352
+
353
+ fi = slice.modih[n].eval(y)
354
+ fidot = dot(slice.modih[n]).eval(y)
355
+
356
+ psi = slice.modie[n].eval(y)
357
+ psisueps = sueps(slice.modie[n]).eval(y)
358
+ psidotsueps = sueps(dot(slice.modie[n])).eval(y)
359
+
360
+ kfh = self.modih[n].k[mx]
361
+ kxh = scipy.sqrt(kfh**2 - kz**2)
362
+ sl = self.modih[n].sl[mx] * (k0 / kfh) ** 2
363
+ al = self.modih[n].al[mx]
364
+ sr = self.modih[n].sr[mx] * (k0 / kfh) ** 2
365
+ ar = self.modih[n].ar[mx]
366
+
367
+ uh[n, idx] = (
368
+ numpy.sin(kxh * x1) * sl + numpy.sin(kxh * x2) * sr
369
+ ) / numpy.sin(kxh * dx)
370
+ udoth[n, idx] = (
371
+ numpy.sin(kxh * x1) * al + numpy.sin(kxh * x2) * ar
372
+ ) / numpy.sin(kxh * dx)
373
+
374
+ kfe = self.modie[n].k[mx]
375
+ kxe = scipy.sqrt(kfe**2 - kz**2)
376
+ sl = self.modie[n].sl[mx] * (k0 / kfe) ** 2
377
+ al = self.modie[n].al[mx]
378
+ sr = self.modie[n].sr[mx] * (k0 / kfe) ** 2
379
+ ar = self.modie[n].ar[mx]
380
+
381
+ ue[n, idx] = (
382
+ numpy.sin(kxe * x1) * sl + numpy.sin(kxe * x2) * sr
383
+ ) / numpy.sin(kxe * dx)
384
+ udote[n, idx] = (
385
+ numpy.sin(kxe * x1) * al + numpy.sin(kxe * x2) * ar
386
+ ) / numpy.sin(kxe * dx)
387
+
388
+ Exsh[:, n] = (kz / k0) * fi
389
+ Exah[:, n] = 0
390
+ Exse[:, n] = 0
391
+ Exae[:, n] = -psidotsueps / k0**2
392
+
393
+ Eysh[:, n] = 0
394
+ Eyah[:, n] = 0
395
+ Eyse[:, n] = -((kfe / k0) ** 2) * psisueps
396
+ Eyae[:, n] = 0
397
+
398
+ Ezsh[:, n] = 0
399
+ Ezah[:, n] = -1j * fi / k0
400
+ Ezse[:, n] = 1j * kz / k0**2 * psidotsueps
401
+ Ezae[:, n] = 0
402
+
403
+ cBxsh[:, n] = 0
404
+ cBxah[:, n] = fidot / k0**2
405
+ cBxse[:, n] = kz / k0 * psi
406
+ cBxae[:, n] = 0
407
+
408
+ cBysh[:, n] = (kfh / k0) ** 2 * fi
409
+ cByah[:, n] = 0
410
+ cByse[:, n] = 0
411
+ cByae[:, n] = 0
412
+
413
+ cBzsh[:, n] = -1j * kz / k0**2 * fidot
414
+ cBzah[:, n] = 0
415
+ cBzse[:, n] = 0
416
+ cBzae[:, n] = -1j * psi / k0
417
+
418
+ ExTE[:, idx] = numpy.tensordot(Exsh, uh[:, idx], axes=1) + numpy.tensordot(
419
+ Exah, udoth[:, idx], axes=1
420
+ )
421
+ ExTM[:, idx] = numpy.tensordot(Exse, ue[:, idx], axes=1) + numpy.tensordot(
422
+ Exae, udote[:, idx], axes=1
423
+ )
424
+
425
+ EyTE[:, idx] = numpy.tensordot(Eysh, uh[:, idx], axes=1) + numpy.tensordot(
426
+ Eyah, udoth[:, idx], axes=1
427
+ )
428
+ EyTM[:, idx] = numpy.tensordot(Eyse, ue[:, idx], axes=1) + numpy.tensordot(
429
+ Eyae, udote[:, idx], axes=1
430
+ )
431
+
432
+ EzTE[:, idx] = numpy.tensordot(Ezsh, uh[:, idx], axes=1) + numpy.tensordot(
433
+ Ezah, udoth[:, idx], axes=1
434
+ )
435
+ EzTM[:, idx] = numpy.tensordot(Ezse, ue[:, idx], axes=1) + numpy.tensordot(
436
+ Ezae, udote[:, idx], axes=1
437
+ )
438
+
439
+ cBxTE[:, idx] = numpy.tensordot(
440
+ cBxsh, uh[:, idx], axes=1
441
+ ) + numpy.tensordot(cBxah, udoth[:, idx], axes=1)
442
+ cBxTM[:, idx] = numpy.tensordot(
443
+ cBxse, ue[:, idx], axes=1
444
+ ) + numpy.tensordot(cBxae, udote[:, idx], axes=1)
445
+
446
+ cByTE[:, idx] = numpy.tensordot(
447
+ cBysh, uh[:, idx], axes=1
448
+ ) + numpy.tensordot(cByah, udoth[:, idx], axes=1)
449
+ cByTM[:, idx] = numpy.tensordot(
450
+ cByse, ue[:, idx], axes=1
451
+ ) + numpy.tensordot(cByae, udote[:, idx], axes=1)
452
+
453
+ cBzTE[:, idx] = numpy.tensordot(
454
+ cBzsh, uh[:, idx], axes=1
455
+ ) + numpy.tensordot(cBzah, udoth[:, idx], axes=1)
456
+ cBzTM[:, idx] = numpy.tensordot(
457
+ cBzse, ue[:, idx], axes=1
458
+ ) + numpy.tensordot(cBzae, udote[:, idx], axes=1)
459
+
460
+ return (
461
+ ExTE,
462
+ ExTM,
463
+ EyTE,
464
+ EyTM,
465
+ EzTE,
466
+ EzTM,
467
+ cBxTE,
468
+ cBxTM,
469
+ cByTE,
470
+ cByTM,
471
+ cBzTE,
472
+ cBzTM,
473
+ )
474
+
475
+ def fields(self, x=None, y=None):
476
+ (
477
+ ExTE,
478
+ ExTM,
479
+ EyTE,
480
+ EyTM,
481
+ EzTE,
482
+ EzTM,
483
+ cBxTE,
484
+ cBxTM,
485
+ cByTE,
486
+ cByTM,
487
+ cBzTE,
488
+ cBzTM,
489
+ ) = self.eval(x, y)
490
+ Ex = ExTE + ExTM
491
+ Ey = EyTE + EyTM
492
+ Ez = EzTE + EzTM
493
+ cBx = cBxTE + cBxTM
494
+ cBy = cByTE + cByTM
495
+ cBz = cBzTE + cBzTM
496
+ return (Ex, Ey, Ez, cBx, cBy, cBz)
497
+
498
+ def intensity(self, x=None, y=None):
499
+ Ex, Ey, Ez, cBx, cBy, cBz = self.fields(x, y)
500
+ cSz = 0.5 * (Ex * numpy.conj(cBy) - Ey * numpy.conj(cBx))
501
+ return cSz
502
+
503
+ def TEfrac_old(self, x_=None, y_=None):
504
+
505
+ if x_ is None:
506
+ x = self.get_x()
507
+ else:
508
+ x = numpy.atleast_1d(x_)
509
+ if y_ is None:
510
+ y = self.get_y()
511
+ else:
512
+ y = numpy.atleast_1d(y_)
513
+
514
+ Ex, Ey, Ez, cBx, cBy, cBz, cSz = self.fields(x, y)
515
+ cSTE = 0.5 * EMpy.utils.trapz2(Ex * numpy.conj(cBy), y, x)
516
+ cSTM = 0.5 * EMpy.utils.trapz2(-Ey * numpy.conj(cBx), y, x)
517
+ return numpy.abs(cSTE) / (numpy.abs(cSTE) + numpy.abs(cSTM))
518
+
519
+ def TEfrac(self):
520
+
521
+ Sx, Sy = self.__overlap(self)
522
+ return Sx / (Sx - Sy)
523
+
524
+ def overlap_old(self, m, x_=None, y_=None):
525
+
526
+ if x_ is None:
527
+ x = self.get_x()
528
+ else:
529
+ x = numpy.atleast_1d(x_)
530
+ if y_ is None:
531
+ y = self.get_y()
532
+ else:
533
+ y = numpy.atleast_1d(y_)
534
+
535
+ Ex, Ey, Ez, cBx, cBy, cBz = self.fields(x, y)
536
+ cSz = self.intensity(x, y)
537
+ norm = scipy.sqrt(EMpy.utils.trapz2(cSz, y, x))
538
+ Ex1, Ey1, Ez1, cBx1, cBy1, cBz1 = m.fields(x, y)
539
+ cSz1 = m.intensity(x, y)
540
+ norm1 = scipy.sqrt(EMpy.utils.trapz2(cSz1, y, x))
541
+ return 0.5 * EMpy.utils.trapz2(
542
+ Ex / norm * numpy.conj(cBy1 / norm1) - Ey / norm * numpy.conj(cBx1 / norm1),
543
+ y,
544
+ x,
545
+ )
546
+
547
+ def __overlap_old(self, mode):
548
+
549
+ nmodi = len(self.modie)
550
+ k0 = 2.0 * numpy.pi / self.slicesx[0].wl
551
+ kz = self.keff
552
+
553
+ Sx = 0j
554
+ Sy = 0j
555
+
556
+ for mx, slice in enumerate(self.slicesx):
557
+
558
+ for n1 in range(nmodi):
559
+
560
+ phi_n1 = slice.modih[n1]
561
+ # phidot_n1 = dot(phi_n1)
562
+
563
+ psi_n1 = slice.modie[n1]
564
+ psisueps_n1 = sueps(psi_n1)
565
+ psidotsueps_n1 = sueps(dot(psi_n1))
566
+
567
+ uh_n1 = copy.deepcopy(self.modih[n1])
568
+ # reduce to a single slice
569
+ kfh_n1 = uh_n1.k[mx]
570
+ uh_n1.k = numpy.atleast_1d(scipy.sqrt(kfh_n1**2 - kz**2))
571
+ uh_n1.sl = numpy.atleast_1d(uh_n1.sl[mx] * (k0 / kfh_n1) ** 2)
572
+ uh_n1.al = numpy.atleast_1d(uh_n1.al[mx])
573
+ uh_n1.sr = numpy.atleast_1d(uh_n1.sr[mx] * (k0 / kfh_n1) ** 2)
574
+ uh_n1.ar = numpy.atleast_1d(uh_n1.ar[mx])
575
+ uh_n1.U = numpy.atleast_1d(uh_n1.U[mx : mx + 2])
576
+ # uhdot_n1 = dot(uh_n1)
577
+
578
+ ue_n1 = copy.deepcopy(self.modie[n1])
579
+ # reduce to a single slice
580
+ kfe_n1 = ue_n1.k[mx]
581
+ ue_n1.k = numpy.atleast_1d(scipy.sqrt(kfe_n1**2 - kz**2))
582
+ ue_n1.sl = numpy.atleast_1d(ue_n1.sl[mx] * (k0 / kfe_n1) ** 2)
583
+ ue_n1.al = numpy.atleast_1d(ue_n1.al[mx])
584
+ ue_n1.sr = numpy.atleast_1d(ue_n1.sr[mx] * (k0 / kfe_n1) ** 2)
585
+ ue_n1.ar = numpy.atleast_1d(ue_n1.ar[mx])
586
+ ue_n1.U = numpy.atleast_1d(ue_n1.U[mx : mx + 2])
587
+ uedot_n1 = dot(ue_n1)
588
+
589
+ for n2 in range(nmodi):
590
+
591
+ phi_n2 = mode.slicesx[mx].modih[n2]
592
+ phidot_n2 = dot(phi_n2)
593
+
594
+ psi_n2 = mode.slicesx[mx].modie[n2]
595
+ # psisueps_n2 = sueps(psi_n2)
596
+ # psidotsueps_n2 = sueps(dot(psi_n2))
597
+
598
+ uh_n2 = copy.deepcopy(mode.modih[n2])
599
+ # reduce to a single slice
600
+ kfh_n2 = uh_n2.k[mx]
601
+ uh_n2.k = numpy.atleast_1d(scipy.sqrt(kfh_n2**2 - kz**2))
602
+ uh_n2.sl = numpy.atleast_1d(uh_n2.sl[mx] * (k0 / kfh_n2) ** 2)
603
+ uh_n2.al = numpy.atleast_1d(uh_n2.al[mx])
604
+ uh_n2.sr = numpy.atleast_1d(uh_n2.sr[mx] * (k0 / kfh_n2) ** 2)
605
+ uh_n2.ar = numpy.atleast_1d(uh_n2.ar[mx])
606
+ uh_n2.U = numpy.atleast_1d(uh_n2.U[mx : mx + 2])
607
+ uhdot_n2 = dot(uh_n2)
608
+
609
+ ue_n2 = copy.deepcopy(mode.modie[n2])
610
+ # reduce to a single slice
611
+ kfe_n2 = ue_n2.k[mx]
612
+ ue_n2.k = numpy.atleast_1d(scipy.sqrt(kfe_n2**2 - kz**2))
613
+ ue_n2.sl = numpy.atleast_1d(ue_n2.sl[mx] * (k0 / kfe_n2) ** 2)
614
+ ue_n2.al = numpy.atleast_1d(ue_n2.al[mx])
615
+ ue_n2.sr = numpy.atleast_1d(ue_n2.sr[mx] * (k0 / kfe_n2) ** 2)
616
+ ue_n2.ar = numpy.atleast_1d(ue_n2.ar[mx])
617
+ ue_n2.U = numpy.atleast_1d(ue_n2.U[mx : mx + 2])
618
+ # uedot_n2 = dot(ue_n2)
619
+
620
+ Sx += kz * kfh_n2**2 / k0**3 * scalarprod(
621
+ uh_n1, uh_n2
622
+ ) * scalarprod(phi_n1, phi_n2) - kfh_n2**2 / k0**4 * scalarprod(
623
+ uedot_n1, uh_n2
624
+ ) * scalarprod(
625
+ psidotsueps_n1, phi_n2
626
+ )
627
+ Sy += kfe_n1**2 * kz / k0**3 * scalarprod(
628
+ ue_n1, ue_n2
629
+ ) * scalarprod(
630
+ psisueps_n1, psi_n2
631
+ ) + kfe_n1**2 / k0**4 * scalarprod(
632
+ ue_n1, uhdot_n2
633
+ ) * scalarprod(
634
+ psisueps_n1, phidot_n2
635
+ )
636
+
637
+ return (Sx, Sy)
638
+
639
+ def __overlap(self, mode):
640
+
641
+ nmodi = len(self.modie)
642
+ k0 = 2.0 * numpy.pi / self.slicesx[0].wl
643
+ kz = self.keff
644
+
645
+ Sx = 0j
646
+ Sy = 0j
647
+
648
+ for mx, slice in enumerate(self.slicesx):
649
+
650
+ phi_n1s = []
651
+ phidot_n1s = []
652
+ psi_n1s = []
653
+ psisueps_n1s = []
654
+ psidotsueps_n1s = []
655
+ uh_n1s = []
656
+ uhdot_n1s = []
657
+ ue_n1s = []
658
+ uedot_n1s = []
659
+ kfe_n1s = []
660
+ kfh_n1s = []
661
+
662
+ phi_n2s = []
663
+ phidot_n2s = []
664
+ psi_n2s = []
665
+ psisueps_n2s = []
666
+ psidotsueps_n2s = []
667
+ uh_n2s = []
668
+ uhdot_n2s = []
669
+ ue_n2s = []
670
+ uedot_n2s = []
671
+ kfe_n2s = []
672
+ kfh_n2s = []
673
+
674
+ for n1 in range(nmodi):
675
+
676
+ phi_n1 = slice.modih[n1]
677
+ phi_n1s.append(phi_n1)
678
+ phidot_n1s.append(dot(phi_n1))
679
+
680
+ psi_n1 = slice.modie[n1]
681
+ psi_n1s.append(psi_n1)
682
+ psisueps_n1s.append(sueps(psi_n1))
683
+ psidotsueps_n1s.append(sueps(dot(psi_n1)))
684
+
685
+ uh_n1 = copy.deepcopy(self.modih[n1])
686
+ # reduce to a single slice
687
+ kfh_n1 = uh_n1.k[mx]
688
+ kfh_n1s.append(kfh_n1)
689
+ uh_n1.k = numpy.atleast_1d(scipy.sqrt(kfh_n1**2 - kz**2))
690
+ uh_n1.sl = numpy.atleast_1d(uh_n1.sl[mx] * (k0 / kfh_n1) ** 2)
691
+ uh_n1.al = numpy.atleast_1d(uh_n1.al[mx])
692
+ uh_n1.sr = numpy.atleast_1d(uh_n1.sr[mx] * (k0 / kfh_n1) ** 2)
693
+ uh_n1.ar = numpy.atleast_1d(uh_n1.ar[mx])
694
+ uh_n1.U = numpy.atleast_1d(uh_n1.U[mx : mx + 2])
695
+ uh_n1s.append(uh_n1)
696
+ uhdot_n1s.append(dot(uh_n1))
697
+
698
+ ue_n1 = copy.deepcopy(self.modie[n1])
699
+ # reduce to a single slice
700
+ kfe_n1 = ue_n1.k[mx]
701
+ kfe_n1s.append(kfe_n1)
702
+ ue_n1.k = numpy.atleast_1d(scipy.sqrt(kfe_n1**2 - kz**2))
703
+ ue_n1.sl = numpy.atleast_1d(ue_n1.sl[mx] * (k0 / kfe_n1) ** 2)
704
+ ue_n1.al = numpy.atleast_1d(ue_n1.al[mx])
705
+ ue_n1.sr = numpy.atleast_1d(ue_n1.sr[mx] * (k0 / kfe_n1) ** 2)
706
+ ue_n1.ar = numpy.atleast_1d(ue_n1.ar[mx])
707
+ ue_n1.U = numpy.atleast_1d(ue_n1.U[mx : mx + 2])
708
+ ue_n1s.append(ue_n1)
709
+ uedot_n1s.append(dot(ue_n1))
710
+
711
+ phi_n2 = mode.slicesx[mx].modih[n1]
712
+ phi_n2s.append(phi_n2)
713
+ phidot_n2s.append(dot(phi_n2))
714
+
715
+ psi_n2 = mode.slicesx[mx].modie[n1]
716
+ psi_n2s.append(psi_n2)
717
+ psisueps_n2s.append(sueps(psi_n2))
718
+ psidotsueps_n2s.append(sueps(dot(psi_n2)))
719
+
720
+ uh_n2 = copy.deepcopy(mode.modih[n1])
721
+ # reduce to a single slice
722
+ kfh_n2 = uh_n2.k[mx]
723
+ kfh_n2s.append(kfh_n2)
724
+ uh_n2.k = numpy.atleast_1d(scipy.sqrt(kfh_n2**2 - kz**2))
725
+ uh_n2.sl = numpy.atleast_1d(uh_n2.sl[mx] * (k0 / kfh_n2) ** 2)
726
+ uh_n2.al = numpy.atleast_1d(uh_n2.al[mx])
727
+ uh_n2.sr = numpy.atleast_1d(uh_n2.sr[mx] * (k0 / kfh_n2) ** 2)
728
+ uh_n2.ar = numpy.atleast_1d(uh_n2.ar[mx])
729
+ uh_n2.U = numpy.atleast_1d(uh_n2.U[mx : mx + 2])
730
+ uh_n2s.append(uh_n2)
731
+ uhdot_n2s.append(dot(uh_n2))
732
+
733
+ ue_n2 = copy.deepcopy(mode.modie[n1])
734
+ # reduce to a single slice
735
+ kfe_n2 = ue_n2.k[mx]
736
+ kfe_n2s.append(kfe_n2)
737
+ ue_n2.k = numpy.atleast_1d(scipy.sqrt(kfe_n2**2 - kz**2))
738
+ ue_n2.sl = numpy.atleast_1d(ue_n2.sl[mx] * (k0 / kfe_n2) ** 2)
739
+ ue_n2.al = numpy.atleast_1d(ue_n2.al[mx])
740
+ ue_n2.sr = numpy.atleast_1d(ue_n2.sr[mx] * (k0 / kfe_n2) ** 2)
741
+ ue_n2.ar = numpy.atleast_1d(ue_n2.ar[mx])
742
+ ue_n2.U = numpy.atleast_1d(ue_n2.U[mx : mx + 2])
743
+ ue_n2s.append(ue_n2)
744
+ uedot_n2s.append(dot(ue_n2))
745
+
746
+ for n1 in range(nmodi):
747
+
748
+ uh_n1 = uh_n1s[n1]
749
+ ue_n1 = ue_n1s[n1]
750
+ uedot_n1 = uedot_n1s[n1]
751
+ phi_n1 = phi_n1s[n1]
752
+ psi_n1 = psi_n1s[n1]
753
+ psidotsueps_n1 = psidotsueps_n1s[n1]
754
+ psisueps_n1 = psisueps_n1s[n1]
755
+ kfe_n1 = kfe_n1s[n1]
756
+
757
+ for n2 in range(nmodi):
758
+
759
+ uh_n2 = uh_n2s[n2]
760
+ uhdot_n2 = uhdot_n2s[n2]
761
+ ue_n2 = ue_n2s[n2]
762
+ phi_n2 = phi_n2s[n2]
763
+ phidot_n2 = phidot_n2s[n2]
764
+ psi_n2 = psi_n2s[n2]
765
+ kfh_n2 = kfh_n2s[n2]
766
+
767
+ Sx += kz * kfh_n2**2 / k0**3 * scalarprod(
768
+ uh_n1, uh_n2
769
+ ) * scalarprod(phi_n1, phi_n2) - kfh_n2**2 / k0**4 * scalarprod(
770
+ uedot_n1, uh_n2
771
+ ) * scalarprod(
772
+ psidotsueps_n1, phi_n2
773
+ )
774
+ Sy += kfe_n1**2 * kz / k0**3 * scalarprod(
775
+ ue_n1, ue_n2
776
+ ) * scalarprod(
777
+ psisueps_n1, psi_n2
778
+ ) + kfe_n1**2 / k0**4 * scalarprod(
779
+ ue_n1, uhdot_n2
780
+ ) * scalarprod(
781
+ psisueps_n1, phidot_n2
782
+ )
783
+
784
+ return (Sx, Sy)
785
+
786
+ def overlap(self, mode):
787
+
788
+ Sx, Sy = self.__overlap(mode)
789
+ return Sx - Sy
790
+
791
+ def norm(self):
792
+
793
+ return scipy.sqrt(self.overlap(self))
794
+
795
+ def normalize(self):
796
+
797
+ n = self.norm()
798
+ for ue, uh in zip(self.modie, self.modih):
799
+ ue.sl /= n
800
+ ue.al /= n
801
+ ue.sr /= n
802
+ ue.ar /= n
803
+ uh.sl /= n
804
+ uh.al /= n
805
+ uh.sr /= n
806
+ uh.ar /= n
807
+
808
+ def get_fields_for_FDTD(self, x, y):
809
+ """Get mode's field on a staggered grid.
810
+
811
+ Note: ignores some fields on the boudaries.
812
+
813
+ """
814
+
815
+ x0 = self.get_x()
816
+ y0 = self.get_y()
817
+ Ex, Ey, Ez, cBx, cBy, cBz = self.fields(x0, y0)
818
+
819
+ # Ex: ignores y = 0, max
820
+ x_Ex_FDTD = EMpy.utils.centered1d(x)
821
+ y_Ex_FDTD = y[1:-1]
822
+ Ex_FDTD = EMpy.utils.interp2(x_Ex_FDTD, y_Ex_FDTD, x0, y0, Ex)
823
+ # Ey: ignores x = 0, max
824
+ x_Ey_FDTD = x[1:-1]
825
+ y_Ey_FDTD = EMpy.utils.centered1d(y)
826
+ Ey_FDTD = EMpy.utils.interp2(x_Ey_FDTD, y_Ey_FDTD, x0, y0, Ey)
827
+ # Ez: ignores x, y = 0, max
828
+ x_Ez_FDTD = x[1:-1]
829
+ y_Ez_FDTD = y[1:-1]
830
+ Ez_FDTD = EMpy.utils.interp2(x_Ez_FDTD, y_Ez_FDTD, x0, y0, Ez)
831
+ # Hx: ignores x = 0, max, /120pi, reverse direction
832
+ x_Hx_FDTD = x[1:-1]
833
+ y_Hx_FDTD = EMpy.utils.centered1d(y)
834
+ Hx_FDTD = EMpy.utils.interp2(x_Hx_FDTD, y_Hx_FDTD, x0, y0, cBx) / (
835
+ -120.0 * numpy.pi
836
+ ) # OKKIO!
837
+ # Hy: ignores y = 0, max, /120pi, reverse direction
838
+ x_Hy_FDTD = EMpy.utils.centered1d(x)
839
+ y_Hy_FDTD = y[1:-1]
840
+ Hy_FDTD = EMpy.utils.interp2(x_Hy_FDTD, y_Hy_FDTD, x0, y0, cBy) / (
841
+ -120.0 * numpy.pi
842
+ )
843
+ # Hz: /120pi, reverse direction
844
+ x_Hz_FDTD = EMpy.utils.centered1d(x)
845
+ y_Hz_FDTD = EMpy.utils.centered1d(y)
846
+ Hz_FDTD = EMpy.utils.interp2(x_Hz_FDTD, y_Hz_FDTD, x0, y0, cBz) / (
847
+ -120.0 * numpy.pi
848
+ )
849
+
850
+ return (Ex_FDTD, Ey_FDTD, Ez_FDTD, Hx_FDTD, Hy_FDTD, Hz_FDTD)
851
+
852
+ def plot(self, x_=None, y_=None):
853
+
854
+ if x_ is None:
855
+ x = self.get_x()
856
+ else:
857
+ x = numpy.atleast_1d(x_)
858
+ if y_ is None:
859
+ y = self.get_y()
860
+ else:
861
+ y = numpy.atleast_1d(y_)
862
+
863
+ f = self.fields(x, y)
864
+
865
+ # fields
866
+ pylab.figure()
867
+ titles = ["Ex", "Ey", "Ez", "cBx", "cBy", "cBz"]
868
+ for i in range(6):
869
+ subplot_id = 231 + i
870
+ pylab.subplot(subplot_id)
871
+ pylab.contour(x, y, numpy.abs(f[i]))
872
+ pylab.xlabel("x")
873
+ pylab.ylabel("y")
874
+ pylab.title(titles[i])
875
+ pylab.axis("image")
876
+ pylab.show()
877
+
878
+ # power
879
+ pylab.figure()
880
+ pylab.contour(x, y, numpy.abs(f[-1]))
881
+ pylab.xlabel("x")
882
+ pylab.ylabel("y")
883
+ pylab.title("cSz")
884
+ pylab.axis("image")
885
+ pylab.show()
886
+
887
+ def __str__(self):
888
+ return "neff = %s" % (self.keff / (2 * numpy.pi / self.slicesx[0].wl))
889
+
890
+
891
+ class FMM(ModeSolver):
892
+ pass
893
+
894
+
895
+ class FMM1d(FMM):
896
+ """Drive to simulate 1d structures.
897
+
898
+ Examples
899
+ ========
900
+
901
+ Find the first 3 TE modes of two slabs of refractive indeces 1 and 3,
902
+ of thickness 1um each, for wl = 1, with symmetric boundary conditions:
903
+
904
+ >>> import numpy
905
+ >>> import FMM
906
+ >>> Uy = numpy.array([0., 1., 2.])
907
+ >>> ny = numpy.array([1., 3.])
908
+ >>> wl = 1.
909
+ >>> nmodi = 3
910
+ >>> simul = FMM.FMM1d(Uy, ny, 'SS').solve(wl, nmodi, 'TE')
911
+ >>> keff_0_expected = 18.790809413149393
912
+ >>> keff_1_expected = 18.314611633384185
913
+ >>> keff_2_expected = 17.326387847565034
914
+ >>> assert(numpy.allclose(simul.modes[0].keff, keff_0_expected))
915
+ >>> assert(numpy.allclose(simul.modes[1].keff, keff_1_expected))
916
+ >>> assert(numpy.allclose(simul.modes[2].keff, keff_2_expected))
917
+
918
+ """
919
+
920
+ def __init__(self, Uy, ny, boundary):
921
+ """Set coordinates of regions, refractive indeces and boundary conditions."""
922
+ self.Uy = Uy
923
+ self.ny = ny
924
+ self.boundary = boundary
925
+
926
+ def solve(self, wl, nmodes, polarization, verbosity=0):
927
+ """Find nmodes modes at a given wavelength and polarization."""
928
+ Message("Solving 1d modes.", 1).show(verbosity)
929
+ self.wl = wl
930
+ self.nmodes = nmodes
931
+ self.polarization = polarization
932
+ self.modes = FMM1d_y(
933
+ self.Uy,
934
+ self.ny,
935
+ self.wl,
936
+ self.nmodes,
937
+ self.boundary,
938
+ self.polarization,
939
+ verbosity,
940
+ )
941
+ return self
942
+
943
+
944
+ class FMM2d(FMM):
945
+ """Drive to simulate 2d structures.
946
+
947
+ Examples
948
+ ========
949
+
950
+ Find the first 2 modes of a lossy Si channel waveguide in SiO2, using
951
+ only 3 1dmodes and with electric and magnetic bc on x and y, respectively:
952
+
953
+ >>> import numpy
954
+ >>> import FMM
955
+ >>> wl = 1.55
956
+ >>> nmodislices = 3
957
+ >>> nmodi2d = 2
958
+ >>> Ux = numpy.array([0, 2, 2.4, 4.4])
959
+ >>> Uy = numpy.array([0, 2, 2.22, 4.22])
960
+ >>> boundary = Boundary(xleft='Electric Wall',
961
+ yleft='Magnetic Wall',
962
+ xright='Electric Wall',
963
+ yright='Magnetic Wall')
964
+ >>> n2 = 1.446
965
+ >>> n1 = 3.4757 - 1e-4j
966
+ >>> refindex = numpy.array([[n2, n2, n2],
967
+ [n2, n1, n2],
968
+ [n2, n2, n2]])
969
+ >>> simul = FMM.FMM2d(Ux, Uy, refindex, boundary).solve(wl, nmodislices, nmodi2d)
970
+ >>> keff0_expected = 9.666663697969399e+000 -4.028846755836984e-004j
971
+ >>> keff1_expected = 7.210476803133368e+000 -2.605078086535284e-004j
972
+ >>> assert(numpy.allclose(simul.modes[0].keff, keff0_expected))
973
+ >>> assert(numpy.allclose(simul.modes[1].keff, keff1_expected))
974
+
975
+ """
976
+
977
+ def __init__(self, Ux, Uy, rix, boundary):
978
+ """Set coordinates of regions, refractive indeces and boundary conditions."""
979
+ self.Ux = Ux
980
+ self.Uy = Uy
981
+ self.rix = rix
982
+ self.boundary = boundary
983
+
984
+ def solve(self, wl, n1dmodes, nmodes, verbosity=0):
985
+ """Find nmodes modes at a given wavelength using n1dmodes 1d modes in each slice."""
986
+ Message("Solving 2d modes", 1).show(verbosity)
987
+ self.wl = wl
988
+ self.n1dmodes = n1dmodes
989
+ self.nmodes = nmodes
990
+ self.slices = script1d(
991
+ self.Ux, self.Uy, self.rix, self.wl, self.boundary, self.n1dmodes, verbosity
992
+ )
993
+ self.modes = FMM1d_x_component(self.slices, nmodes, verbosity)
994
+ return self
995
+
996
+
997
+ def analyticalsolution(nmodi, TETM, FMMpars):
998
+
999
+ betay = FMMpars["beta"]
1000
+ epsilon = FMMpars["epsilon"]
1001
+ Uy = FMMpars["Uy"]
1002
+ by = FMMpars["boundary"]
1003
+
1004
+ Nregions = len(epsilon)
1005
+ sl = numpy.zeros((nmodi, Nregions), dtype=complex)
1006
+ sr = numpy.zeros_like(sl)
1007
+ al = numpy.zeros_like(sl)
1008
+ ar = numpy.zeros_like(sl)
1009
+ # interval
1010
+ D = Uy[-1] - Uy[0]
1011
+ if TETM == "TE":
1012
+ N = numpy.sqrt(2.0 / D)
1013
+ else:
1014
+ N = numpy.sqrt(2.0 / D * epsilon[0])
1015
+
1016
+ # boundary condition
1017
+ if by == "AA":
1018
+ kn = numpy.pi * numpy.arange(1, nmodi + 1) / D
1019
+ kn = kn[:, numpy.newaxis]
1020
+ sl = numpy.sin(kn * (Uy[:-1] - Uy[0]))
1021
+ sr = numpy.sin(kn * (Uy[1:] - Uy[0]))
1022
+ al = numpy.cos(kn * (Uy[:-1] - Uy[0]))
1023
+ ar = numpy.cos(kn * (Uy[1:] - Uy[0]))
1024
+ sr[:, -1] = 0.0
1025
+ sl[:, 0] = 0.0
1026
+ elif by == "AS":
1027
+ kn = numpy.pi * (numpy.arange(0, nmodi) + 0.5) / D
1028
+ kn = kn[:, numpy.newaxis]
1029
+ sl = numpy.sin(kn * (Uy[:-1] - Uy[0]))
1030
+ sr = numpy.sin(kn * (Uy[1:] - Uy[0]))
1031
+ al = numpy.cos(kn * (Uy[:-1] - Uy[0]))
1032
+ ar = numpy.cos(kn * (Uy[1:] - Uy[0]))
1033
+ ar[:, -1] = 0.0
1034
+ sl[:, 0] = 0.0
1035
+ elif by == "SA":
1036
+ kn = numpy.pi * (numpy.arange(0, nmodi) + 0.5) / D
1037
+ kn = kn[:, numpy.newaxis]
1038
+ sl = numpy.cos(kn * (Uy[:-1] - Uy[0]))
1039
+ sr = numpy.cos(kn * (Uy[1:] - Uy[0]))
1040
+ al = -numpy.sin(kn * (Uy[:-1] - Uy[0]))
1041
+ ar = -numpy.sin(kn * (Uy[1:] - Uy[0]))
1042
+ sr[:, -1] = 0.0
1043
+ al[:, 0] = 0.0
1044
+ elif by == "SS":
1045
+ kn = numpy.pi * numpy.arange(0, nmodi) / D
1046
+ kn = kn[:, numpy.newaxis]
1047
+ sl = numpy.cos(kn * (Uy[:-1] - Uy[0]))
1048
+ sr = numpy.cos(kn * (Uy[1:] - Uy[0]))
1049
+ al = -numpy.sin(kn * (Uy[:-1] - Uy[0]))
1050
+ ar = -numpy.sin(kn * (Uy[1:] - Uy[0]))
1051
+ ar[:, -1] = 0.0
1052
+ al[:, 0] = 0.0
1053
+
1054
+ # normalizzazione
1055
+ sl *= N
1056
+ sr *= N
1057
+
1058
+ for n in range(0, nmodi):
1059
+ al[n, :] *= N * kn[n]
1060
+ ar[n, :] *= N * kn[n]
1061
+
1062
+ # caso speciale. se k=0 la funzione e' costante e la normalizzazione e'
1063
+ # diversa. capita solo con boundary SS e per il primo modo
1064
+ if by == "SS":
1065
+ sqrt2 = numpy.sqrt(2.0)
1066
+ sl[0, :] /= sqrt2
1067
+ sr[0, :] /= sqrt2
1068
+ al[0, :] /= sqrt2
1069
+ ar[0, :] /= sqrt2
1070
+
1071
+ modi = []
1072
+ for mk in range(0, nmodi):
1073
+ modo = FMMMode1dy()
1074
+ modo.sl = sl[mk, :].astype(complex)
1075
+ modo.sr = sr[mk, :].astype(complex)
1076
+ modo.al = al[mk, :].astype(complex)
1077
+ modo.ar = ar[mk, :].astype(complex)
1078
+ modo.k = kn[mk] * numpy.ones(Nregions)
1079
+ modo.U = Uy
1080
+ modo.keff = scipy.sqrt(betay[0] ** 2 - kn[mk] ** 2)
1081
+ modo.zero = 0.0
1082
+ modo.pars = FMMpars
1083
+ modi.append(modo)
1084
+ return modi
1085
+
1086
+
1087
+ def sinxsux(x):
1088
+ return numpy.sinc(x / numpy.pi)
1089
+
1090
+
1091
+ def FMMshootingTM(kz_, FMMpars):
1092
+
1093
+ betay = FMMpars["beta"]
1094
+ eps = FMMpars["epsilon"]
1095
+ Uy = FMMpars["Uy"]
1096
+ by = FMMpars["boundary"]
1097
+
1098
+ kz = numpy.atleast_1d(kz_)
1099
+
1100
+ Nregions = len(betay)
1101
+ d = numpy.diff(Uy)
1102
+ Delta = numpy.zeros_like(kz)
1103
+
1104
+ sl = numpy.zeros(Nregions, dtype=complex)
1105
+ sr = numpy.zeros_like(sl)
1106
+ al = numpy.zeros_like(sl)
1107
+ ar = numpy.zeros_like(sl)
1108
+
1109
+ k_ = scipy.sqrt(betay**2 - kz[:, numpy.newaxis] ** 2)
1110
+ kd = k_[:, numpy.newaxis] * d
1111
+ sinkdsuk_ = sinxsux(kd) * d
1112
+ coskd_ = numpy.cos(kd)
1113
+ sinkdk_ = numpy.sin(kd) * k_[:, numpy.newaxis]
1114
+
1115
+ # left boundary condition
1116
+ if by[0] == "A":
1117
+ al[0] = 1
1118
+ elif by[0] == "S":
1119
+ sl[0] = 1
1120
+ else:
1121
+ raise ValueError("unrecognized left boundary condition")
1122
+
1123
+ # right boundary condition
1124
+ if by[1] == "A":
1125
+ ar[-1] = 1
1126
+ elif by[1] == "S":
1127
+ sr[-1] = 1
1128
+ else:
1129
+ raise ValueError("unrecognized right boundary condition")
1130
+
1131
+ # ciclo sui layer
1132
+ # maxbetay = numpy.max(numpy.real(betay))
1133
+ n1 = numpy.argmax(numpy.real(betay)) + 1
1134
+ if n1 == Nregions:
1135
+ n1 = Nregions - 1
1136
+ n2 = n1 + 1
1137
+
1138
+ modo = FMMMode1dy()
1139
+
1140
+ for m in range(0, len(kz)):
1141
+
1142
+ k = k_[m, :]
1143
+ sinkdsuk = sinkdsuk_[m, :][0]
1144
+ coskd = coskd_[m, :][0]
1145
+ sinkdk = sinkdk_[m, :][0]
1146
+
1147
+ for idx in range(0, n1):
1148
+
1149
+ sr[idx] = sl[idx] * coskd[idx] + al[idx] * sinkdsuk[idx]
1150
+ ar[idx] = al[idx] * coskd[idx] - sl[idx] * sinkdk[idx]
1151
+
1152
+ # ******************* requirement of continuity
1153
+ if idx < n1 - 1:
1154
+ sl[idx + 1] = sr[idx]
1155
+ al[idx + 1] = ar[idx] / eps[idx] * eps[idx + 1]
1156
+ # *******************
1157
+
1158
+ for idx1 in range(Nregions - 1, n2 - 2, -1):
1159
+
1160
+ sl[idx1] = sr[idx1] * coskd[idx1] - ar[idx1] * sinkdsuk[idx1]
1161
+ al[idx1] = ar[idx1] * coskd[idx1] + sr[idx1] * sinkdk[idx1]
1162
+
1163
+ # ******************* requirement of continuity
1164
+ if idx1 > n2:
1165
+ sr[idx1 - 1] = sl[idx1]
1166
+ ar[idx1 - 1] = al[idx1] / eps[idx1] * eps[idx1 - 1]
1167
+ # *******************
1168
+
1169
+ Delta[m] = (
1170
+ eps[n1 - 1] * sr[n1 - 1] * al[n2 - 1]
1171
+ - eps[n2 - 1] * ar[n1 - 1] * sl[n2 - 1]
1172
+ )
1173
+
1174
+ if len(kz) < 2:
1175
+
1176
+ # normalize and save only if len(kz) == 1
1177
+ # otherwise, modo is ignored and only Delta is useful
1178
+
1179
+ # normalizza la propagazione sinistra e quella destra
1180
+ alfa = sr[n1 - 1] / sl[n2 - 1]
1181
+ sl[n2 - 1 :] *= alfa
1182
+ sr[n2 - 1 :] *= alfa
1183
+ al[n2 - 1 :] *= alfa
1184
+ ar[n2 - 1 :] *= alfa
1185
+
1186
+ modo.sl = sl
1187
+ modo.sr = sr
1188
+ modo.al = al
1189
+ modo.ar = ar
1190
+ modo.k = k
1191
+ modo.U = Uy
1192
+ modo.keff = kz
1193
+ modo.zero = Delta
1194
+ modo.pars = FMMpars
1195
+
1196
+ return (Delta, modo)
1197
+
1198
+
1199
+ def FMMshooting(kz_, FMMpars):
1200
+
1201
+ betay = FMMpars["beta"]
1202
+ Uy = FMMpars["Uy"]
1203
+ by = FMMpars["boundary"]
1204
+
1205
+ kz = numpy.atleast_1d(kz_)
1206
+
1207
+ Nregions = len(betay)
1208
+ d = numpy.diff(Uy)
1209
+ Delta = numpy.zeros_like(kz)
1210
+
1211
+ sl = numpy.zeros(Nregions, dtype=complex)
1212
+ sr = numpy.zeros_like(sl)
1213
+ al = numpy.zeros_like(sl)
1214
+ ar = numpy.zeros_like(sl)
1215
+
1216
+ k_ = scipy.sqrt(betay**2 - kz[:, numpy.newaxis] ** 2)
1217
+ kd = k_[:, numpy.newaxis] * d
1218
+ sinkdsuk_ = sinxsux(kd) * d
1219
+ coskd_ = numpy.cos(kd)
1220
+ sinkdk_ = numpy.sin(kd) * k_[:, numpy.newaxis]
1221
+
1222
+ # left boundary condition
1223
+ if by[0] == "A":
1224
+ al[0] = 1
1225
+ elif by[0] == "S":
1226
+ sl[0] = 1
1227
+ else:
1228
+ raise ValueError("unrecognized left boundary condition")
1229
+
1230
+ # right boundary condition
1231
+ if by[1] == "A":
1232
+ ar[-1] = 1
1233
+ elif by[1] == "S":
1234
+ sr[-1] = 1
1235
+ else:
1236
+ raise ValueError("unrecognized right boundary condition")
1237
+
1238
+ # ciclo sui layer
1239
+ # maxbetay = numpy.max(numpy.real(betay))
1240
+ n1 = numpy.argmax(numpy.real(betay)) + 1
1241
+ if n1 == Nregions:
1242
+ n1 = Nregions - 1
1243
+ n2 = n1 + 1
1244
+
1245
+ modo = FMMMode1dy()
1246
+
1247
+ for m in range(0, len(kz)):
1248
+
1249
+ k = k_[m, :]
1250
+ sinkdsuk = sinkdsuk_[m, :][0]
1251
+ coskd = coskd_[m, :][0]
1252
+ sinkdk = sinkdk_[m, :][0]
1253
+
1254
+ for idx in range(0, n1):
1255
+
1256
+ sr[idx] = sl[idx] * coskd[idx] + al[idx] * sinkdsuk[idx]
1257
+ ar[idx] = al[idx] * coskd[idx] - sl[idx] * sinkdk[idx]
1258
+
1259
+ # ******************* requirement of continuity
1260
+ if idx < n1 - 1:
1261
+ sl[idx + 1] = sr[idx]
1262
+ al[idx + 1] = ar[idx]
1263
+ # *******************
1264
+
1265
+ for idx1 in range(Nregions - 1, n2 - 2, -1):
1266
+
1267
+ sl[idx1] = sr[idx1] * coskd[idx1] - ar[idx1] * sinkdsuk[idx1]
1268
+ al[idx1] = ar[idx1] * coskd[idx1] + sr[idx1] * sinkdk[idx1]
1269
+
1270
+ # ******************* requirement of continuity
1271
+ if idx1 > n2:
1272
+ sr[idx1 - 1] = sl[idx1]
1273
+ ar[idx1 - 1] = al[idx1]
1274
+ # *******************
1275
+ Delta[m] = sr[n1 - 1] * al[n2 - 1] - ar[n1 - 1] * sl[n2 - 1]
1276
+
1277
+ # len_kz = len(kz)
1278
+ # k = k_[0,:]
1279
+ # sinkdsuk = sinkdsuk_[0,:][0]
1280
+ # coskd = coskd_[0,:][0]
1281
+ # sinkdk = sinkdk_[0,:][0]
1282
+ # code = """
1283
+ # for (int m = 0; m < len_kz; ++m) {
1284
+ # //k = k_(m,:);
1285
+ # //sinkdsuk = sinkdsuk_(0,:);
1286
+ # //coskd = coskd_(0,:);
1287
+ # //sinkdk = sinkdk_(0,:);
1288
+ # int nn1 = int(n1);
1289
+ # for (int idx = 0; idx < nn1; ++idx) {
1290
+ # sr(idx) = sl(idx) * coskd(idx) + al(idx) * sinkdsuk(idx);
1291
+ # ar(idx) = al(idx) * coskd(idx) - sl(idx) * sinkdk(idx);
1292
+ # if (idx < nn1 - 1) {
1293
+ # sl(idx + 1) = sr(idx);
1294
+ # al(idx + 1) = ar(idx);
1295
+ # }
1296
+ # }
1297
+ # int nn2 = int(n2);
1298
+ # for (int idx1 = Nregions - 1; idx1 > nn2 - 2; --idx1) {
1299
+ # sl(idx1) = sr(idx1) * coskd(idx1) - ar(idx1) * sinkdsuk(idx1);
1300
+ # al(idx1) = ar(idx1) * coskd(idx1) + sr(idx1) * sinkdk(idx1);
1301
+ # if (idx1 > nn2) {
1302
+ # sr(idx1 - 1) = sl(idx1);
1303
+ # ar(idx1 - 1) = al(idx1);
1304
+ # }
1305
+ # }
1306
+ # //Delta(m) = std::complex<double>(1) * (sr(nn1-1) * al(nn2-1) - ar(nn1-1) * sl(nn2-1));
1307
+ # }
1308
+ # """
1309
+ #
1310
+ # from scipy import weave
1311
+ # from scipy.weave import converters
1312
+ # weave.inline(code,
1313
+ # ['n1', 'n2', 'Nregions', 'sl', 'sr', 'al', 'ar', 'len_kz', 'Delta',
1314
+ # 'k', 'sinkdsuk', 'sinkdk', 'coskd',
1315
+ # 'k_', 'sinkdsuk_', 'sinkdk_', 'coskd_'],
1316
+ # type_converters = converters.blitz,
1317
+ # compiler = 'gcc')
1318
+
1319
+ if len(kz) < 2:
1320
+
1321
+ # normalize and save only if len(kz) == 1
1322
+ # otherwise, modo is ignored and only Delta is useful
1323
+
1324
+ # normalizza la propagazione sinistra e quella destra
1325
+ alfa = sr[n1 - 1] / sl[n2 - 1]
1326
+
1327
+ sl[n2 - 1 :] *= alfa
1328
+ sr[n2 - 1 :] *= alfa
1329
+ al[n2 - 1 :] *= alfa
1330
+ ar[n2 - 1 :] *= alfa
1331
+
1332
+ modo.sl = sl
1333
+ modo.sr = sr
1334
+ modo.al = al
1335
+ modo.ar = ar
1336
+ modo.k = k
1337
+ modo.U = Uy
1338
+ modo.keff = kz
1339
+ modo.zero = Delta
1340
+ modo.pars = FMMpars
1341
+
1342
+ return (Delta, modo)
1343
+
1344
+
1345
+ def remove_consecutives(x, y):
1346
+
1347
+ b = numpy.r_[numpy.diff(x) == 1, 0].astype(int)
1348
+
1349
+ ic = 0
1350
+ flag = 0
1351
+ l = []
1352
+ for ib in range(len(b)):
1353
+ if flag == 0:
1354
+ c = [x[ib]]
1355
+ ic += 1
1356
+ if b[ib] == 1:
1357
+ flag = 1
1358
+ else:
1359
+ l.append(c)
1360
+ else:
1361
+ c.append(x[ib])
1362
+ if b[ib] != 1:
1363
+ flag = 0
1364
+ l.append(c)
1365
+
1366
+ index = []
1367
+ for il, ll in enumerate(l):
1368
+ newi = ll
1369
+ itmp = numpy.argmax(y[newi])
1370
+ index.append(newi[0] + itmp)
1371
+
1372
+ return index
1373
+
1374
+
1375
+ def findzerosnew(x, y, searchinterval):
1376
+
1377
+ minsi = 2 * numpy.abs(x[1] - x[0])
1378
+ if searchinterval < minsi:
1379
+ searchinterval = minsi
1380
+
1381
+ dy = numpy.r_[0, numpy.diff(numpy.diff(scipy.log(y))), 0]
1382
+ idy = numpy.where(dy > 0.005)[0]
1383
+
1384
+ if len(idy) == 0:
1385
+ zeri = numpy.array([])
1386
+ z1 = numpy.array([])
1387
+ z2 = numpy.array([])
1388
+ else:
1389
+ ind = remove_consecutives(idy, dy)
1390
+ zeri = x[ind]
1391
+ z1 = numpy.zeros_like(zeri)
1392
+ z2 = numpy.zeros_like(zeri)
1393
+
1394
+ dz = numpy.abs(numpy.diff(zeri))
1395
+ if len(dz) == 0:
1396
+ z1[0] = zeri - searchinterval / 2
1397
+ z2[0] = zeri + searchinterval / 2
1398
+ else:
1399
+ delta = numpy.min([dz[0], searchinterval])
1400
+ z1[0] = zeri[0] - delta / 2
1401
+ z2[0] = zeri[0] + delta / 2
1402
+
1403
+ for idx in range(1, len(zeri) - 1):
1404
+ delta = numpy.min([dz[idx - 1], dz[idx], searchinterval])
1405
+ z1[idx] = zeri[idx] - delta / 2
1406
+ z2[idx] = zeri[idx] + delta / 2
1407
+
1408
+ delta = numpy.min([dz[-1], searchinterval])
1409
+ z1[-1] = zeri[-1] - delta / 2
1410
+ z2[-1] = zeri[-1] + delta / 2
1411
+
1412
+ return (zeri, z1, z2)
1413
+
1414
+
1415
+ def absfzzero2(t, f, xmin, xmax, ymin, ymax):
1416
+
1417
+ xmean = numpy.mean([xmin, xmax])
1418
+ ymean = numpy.mean([ymin, ymax])
1419
+ xwidth = xmax - xmin
1420
+ ywidth = ymax - ymin
1421
+ x = xmean + xwidth * t[0] / (1.0 + numpy.abs(t[0])) / 2.0
1422
+ y = ymean + ywidth * t[1] / (1.0 + numpy.abs(t[1])) / 2.0
1423
+ z = x + 1j * y
1424
+ fv = f(z)
1425
+ return numpy.abs(fv) ** 2
1426
+
1427
+
1428
+ def fzzeroabs2(f, zmin, zmax):
1429
+
1430
+ xmin = numpy.real(zmin)
1431
+ ymin = numpy.imag(zmin)
1432
+ xmax = numpy.real(zmax)
1433
+ ymax = numpy.imag(zmax)
1434
+
1435
+ tx0 = 0.0
1436
+ ty0 = 0.0
1437
+
1438
+ t0 = scipy.optimize.fmin(
1439
+ lambda t: absfzzero2(t, f, xmin, xmax, ymin, ymax),
1440
+ [tx0, ty0],
1441
+ maxiter=100000,
1442
+ maxfun=100000,
1443
+ xtol=1e-15,
1444
+ ftol=1e-15,
1445
+ disp=0,
1446
+ )
1447
+
1448
+ xmean = numpy.mean([xmin, xmax])
1449
+ ymean = numpy.mean([ymin, ymax])
1450
+ xwidth = xmax - xmin
1451
+ ywidth = ymax - ymin
1452
+ x0 = xmean + xwidth * t0[0] / (1 + numpy.abs(t0[0])) / 2
1453
+ y0 = ymean + ywidth * t0[1] / (1 + numpy.abs(t0[1])) / 2
1454
+
1455
+ z0 = x0 + 1j * y0
1456
+ valf = f(z0)
1457
+
1458
+ return (z0, valf)
1459
+
1460
+
1461
+ def scalarprod(modo1, modo2):
1462
+
1463
+ d = numpy.diff(modo1.U)
1464
+
1465
+ ky1 = modo1.k
1466
+ al1 = modo1.al
1467
+ sl1 = modo1.sl
1468
+ ar1 = modo1.ar
1469
+ sr1 = modo1.sr
1470
+
1471
+ ky2 = modo2.k
1472
+ al2 = modo2.al
1473
+ sl2 = modo2.sl
1474
+ ar2 = modo2.ar
1475
+ sr2 = modo2.sr
1476
+
1477
+ Nlayers = len(modo1.sl)
1478
+ scprod = numpy.zeros_like(modo1.sl)
1479
+
1480
+ for idy in range(Nlayers):
1481
+
1482
+ if numpy.allclose(ky1[idy], ky2[idy]):
1483
+ if numpy.linalg.norm(ky1) < 1e-10:
1484
+ scprod[idy] = sl1[idy] * sl2[idy] * (modo1.U[idy + 1] - modo1.U[idy])
1485
+ else:
1486
+ scprod[idy] = (sl1[idy] * al2[idy] - sr1[idy] * ar2[idy]) / ky1[
1487
+ idy
1488
+ ] / ky2[idy] / 2.0 + d[idy] / 2.0 * (
1489
+ sl1[idy] * sl2[idy] + al1[idy] * al2[idy] / ky1[idy] / ky2[idy]
1490
+ )
1491
+ else:
1492
+ if numpy.linalg.norm(ky1) < 1e-10:
1493
+ scprod[idy] = sl1[idy] * (al2[idy] - ar2[idy]) / ky2[idy] ** 2
1494
+ elif numpy.linalg.norm(ky2) < 1e-10:
1495
+ scprod[idy] = sl2[idy] * (al1[idy] - ar1[idy]) / ky1[idy] ** 2
1496
+ else:
1497
+ scprod[idy] = (
1498
+ sr1[idy] * ar2[idy]
1499
+ - ar1[idy] * sr2[idy]
1500
+ - sl1[idy] * al2[idy]
1501
+ + al1[idy] * sl2[idy]
1502
+ ) / (ky1[idy] ** 2 - ky2[idy] ** 2)
1503
+
1504
+ return numpy.sum(scprod)
1505
+
1506
+
1507
+ def sueps(modo):
1508
+
1509
+ eps = modo.pars["epsilon"]
1510
+ modosueps = copy.deepcopy(modo)
1511
+ modosueps.sl /= eps
1512
+ modosueps.sr /= eps
1513
+ modosueps.al /= eps
1514
+ modosueps.ar /= eps
1515
+
1516
+ return modosueps
1517
+
1518
+
1519
+ def FMM1d_y(Uy, ny, wl, nmodi, boundaryRL, TETM, verbosity=0):
1520
+
1521
+ k0 = 2 * numpy.pi / wl
1522
+ betay = ny * k0
1523
+ Nstepskz = 1543
1524
+ searchinterval = max(50.0 / Nstepskz, numpy.abs(numpy.min(numpy.imag(2.0 * betay))))
1525
+ imsearchinterval = 10 * k0
1526
+ # ypointsperregion = 5000
1527
+
1528
+ FMMpars = {"epsilon": ny**2, "beta": betay, "boundary": boundaryRL, "Uy": Uy}
1529
+
1530
+ # analytical solution
1531
+ if numpy.allclose(ny, ny[0]):
1532
+ Message("Uniform slice found: using analytical solution.", 2).show(verbosity)
1533
+ return analyticalsolution(nmodi, TETM, FMMpars)
1534
+
1535
+ # rekz = numpy.linspace(numpy.max(numpy.real(betay)) + searchinterval, 0., Nstepskz)
1536
+ rekz2 = numpy.linspace(
1537
+ (numpy.max(numpy.real(betay)) + searchinterval) ** 2, 0.0, Nstepskz
1538
+ )
1539
+ rekz = scipy.sqrt(rekz2)
1540
+ if TETM == "TM":
1541
+ Message("Shooting TM.", 3).show(verbosity)
1542
+ matchingre, modotmp = FMMshootingTM(rekz, FMMpars)
1543
+ else:
1544
+ Message("Shooting TE.", 3).show(verbosity)
1545
+ matchingre, modotmp = FMMshooting(rekz, FMMpars)
1546
+
1547
+ # nre = rekz / k0
1548
+ nre2 = rekz2 / k0**2
1549
+ # zerire, z1, z2 = findzerosnew(nre, numpy.abs(matchingre), searchinterval / k0)
1550
+ zerire2, z12, z22 = findzerosnew(
1551
+ nre2, numpy.abs(matchingre), (searchinterval / k0) ** 2
1552
+ )
1553
+ zerire = scipy.sqrt(zerire2)
1554
+ kz1 = zerire * k0 - searchinterval / 2.0 + 1j * imsearchinterval
1555
+ kz2 = zerire * k0 + searchinterval / 2.0 - 1j * imsearchinterval
1556
+ Message("Found %d real zeros." % len(zerire), 2).show(verbosity)
1557
+ if len(zerire) < nmodi:
1558
+ Message("Number of real zeros not enough: scan imaginary axis.", 2).show(
1559
+ verbosity
1560
+ )
1561
+ imkza = -numpy.max(numpy.real(betay))
1562
+ imkzb = 0.0
1563
+ while len(kz1) < nmodi:
1564
+ imkza = imkza + numpy.max(numpy.real(betay))
1565
+ imkzb = imkzb + numpy.max(numpy.real(betay))
1566
+ # imkz = numpy.linspace(imkza, imkzb, Nstepskz)
1567
+ imkz2 = numpy.linspace(imkza**2, imkzb**2, Nstepskz)
1568
+ imkz = scipy.sqrt(imkz2)
1569
+ if TETM == "TM":
1570
+ matchingim, modotmp = FMMshootingTM(1j * imkz, FMMpars)
1571
+ else:
1572
+ matchingim, modotmp = FMMshooting(1j * imkz, FMMpars)
1573
+ # nim = imkz * wl / 2.0 / numpy.pi
1574
+ nim2 = imkz2 * (wl / 2.0 / numpy.pi) ** 2
1575
+ # zeriim, z1, z2 = findzerosnew(nim, numpy.abs(matchingim), searchinterval / k0)
1576
+ zeriim2, z12, z22 = findzerosnew(
1577
+ nim2, numpy.abs(matchingim), (searchinterval / k0) ** 2
1578
+ )
1579
+ zeriim = scipy.sqrt(zeriim2)
1580
+ Message("Found %d imag zeros." % len(zeriim), 2).show(verbosity)
1581
+ kz1 = numpy.r_[
1582
+ kz1,
1583
+ 1j * (zeriim * k0 - imsearchinterval / 2.0 + 1j * searchinterval / 2.0),
1584
+ ]
1585
+ kz2 = numpy.r_[
1586
+ kz2,
1587
+ 1j * (zeriim * k0 + imsearchinterval / 2.0 - 1j * searchinterval / 2.0),
1588
+ ]
1589
+
1590
+ mk = 0
1591
+ modi = []
1592
+ # inizia il ciclo sugli intervalli
1593
+ Message("Refine zeros.", 2).show(verbosity)
1594
+ for m in range(0, len(kz1)):
1595
+
1596
+ if mk == nmodi:
1597
+ break
1598
+ if TETM == "TM":
1599
+ z0, valf = fzzeroabs2(
1600
+ lambda kz: FMMshootingTM(kz, FMMpars)[0], kz1[m], kz2[m]
1601
+ )
1602
+ z0 = numpy.atleast_1d(z0)
1603
+ else:
1604
+ z0, valf = fzzeroabs2(
1605
+ lambda kz: FMMshooting(kz, FMMpars)[0], kz1[m], kz2[m]
1606
+ )
1607
+ z0 = numpy.atleast_1d(z0)
1608
+
1609
+ if len(z0) > 0:
1610
+ if TETM == "TM":
1611
+ zero, modo = FMMshootingTM(z0, FMMpars)
1612
+ else:
1613
+ zero, modo = FMMshooting(z0, FMMpars)
1614
+
1615
+ if TETM == "TM":
1616
+ normalizzazione = 1.0 / numpy.sqrt(scalarprod(modo, sueps(modo)))
1617
+ else:
1618
+ normalizzazione = 1.0 / numpy.sqrt(scalarprod(modo, modo))
1619
+
1620
+ modo.sl *= normalizzazione
1621
+ modo.al *= normalizzazione
1622
+ modo.sr *= normalizzazione
1623
+ modo.ar *= normalizzazione
1624
+
1625
+ mk += 1
1626
+ modi.append(modo)
1627
+
1628
+ return modi
1629
+
1630
+
1631
+ def script1d(Ux, Uy, refindex, wl, boundary, nmodislices, verbosity=0):
1632
+
1633
+ nx = refindex.shape[0]
1634
+ slices = []
1635
+ for m in range(nx):
1636
+ Message("Finding 1dmodes TE.", 1).show(verbosity)
1637
+ ymodih = FMM1d_y(
1638
+ Uy, refindex[m, :], wl, nmodislices, boundary.yh, "TE", verbosity
1639
+ )
1640
+ Message("Finding 1dmodes TM.", 1).show(verbosity)
1641
+ ymodie = FMM1d_y(
1642
+ Uy, refindex[m, :], wl, nmodislices, boundary.ye, "TM", verbosity
1643
+ )
1644
+ slice = Slice(
1645
+ x1=Ux[m], x2=Ux[m + 1], Uy=Uy, boundary=boundary, modie=ymodie, modih=ymodih
1646
+ )
1647
+ # OKKIO: do I really need them?
1648
+ slice.Ux = Ux
1649
+ slice.refractiveindex = refindex
1650
+ slice.epsilon = refindex**2
1651
+ slice.wl = wl
1652
+ slices.append(slice)
1653
+ return slices
1654
+
1655
+
1656
+ def dot(modo):
1657
+
1658
+ k = modo.k
1659
+ mododot = copy.deepcopy(modo)
1660
+ mododot.sl = modo.al
1661
+ mododot.sr = modo.ar
1662
+ mododot.al = -(k**2) * modo.sl
1663
+ mododot.ar = -(k**2) * modo.sr
1664
+ return mododot
1665
+
1666
+
1667
+ def genera_rotazione(slices):
1668
+
1669
+ nmodi = len(slices[0].modih)
1670
+ k0 = 2 * numpy.pi / slices[0].wl
1671
+ Nslices = len(slices)
1672
+
1673
+ R = Struct()
1674
+ # alloc R
1675
+ R.Ree = numpy.zeros((nmodi, nmodi, Nslices - 1), dtype=complex)
1676
+ R.Reem = numpy.zeros_like(R.Ree)
1677
+ R.Rhh = numpy.zeros_like(R.Ree)
1678
+ R.Rhhm = numpy.zeros_like(R.Ree)
1679
+ R.Rhe = numpy.zeros_like(R.Ree)
1680
+ R.Rhem = numpy.zeros_like(R.Ree)
1681
+
1682
+ for idx in range(len(slices) - 1):
1683
+ slice = slices[idx]
1684
+ slicep1 = slices[idx + 1]
1685
+
1686
+ for n in range(nmodi):
1687
+ Fhn = slice.modih[n]
1688
+ Fp1hn = slicep1.modih[n]
1689
+ Fen = slice.modie[n]
1690
+ Fp1en = slicep1.modie[n]
1691
+ Fhndot = dot(Fhn)
1692
+ Fp1hndot = dot(Fp1hn)
1693
+ khidx = slice.modih[n].keff
1694
+ khidxp1 = slicep1.modih[n].keff
1695
+
1696
+ for m in range(nmodi):
1697
+ Fem = slice.modie[m]
1698
+ Fhm = slice.modih[m]
1699
+ Fp1em = slicep1.modie[m]
1700
+ Fp1hm = slicep1.modih[m]
1701
+ Femsueps = sueps(Fem)
1702
+ Femdotsueps = dot(Femsueps)
1703
+ Fp1emsueps = sueps(Fp1em)
1704
+ Fp1emdotsueps = dot(Fp1emsueps)
1705
+ keidx = slice.modie[m].keff
1706
+ keidxp1 = slicep1.modie[m].keff
1707
+
1708
+ R.Ree[n, m, idx] = scalarprod(Fen, Fp1emsueps)
1709
+ R.Reem[n, m, idx] = scalarprod(Fp1en, Femsueps)
1710
+ R.Rhh[n, m, idx] = scalarprod(Fhn, Fp1hm)
1711
+ R.Rhhm[n, m, idx] = scalarprod(Fp1hn, Fhm)
1712
+
1713
+ s1 = k0 * scalarprod(Fhndot, Fp1emsueps) / khidx**2
1714
+ s2 = k0 * scalarprod(Fhn, Fp1emdotsueps) / keidxp1**2
1715
+ R.Rhe[n, m, idx] = (s1 + s2).item()
1716
+
1717
+ s1 = k0 * scalarprod(Fp1hndot, Femsueps) / khidxp1**2
1718
+ s2 = k0 * scalarprod(Fp1hn, Femdotsueps) / keidx**2
1719
+ R.Rhem[n, m, idx] = (s1 + s2).item()
1720
+
1721
+ return R
1722
+
1723
+
1724
+ def ortonormalita(slices):
1725
+
1726
+ nmodi = len(slices[0].modih)
1727
+ k0 = 2 * numpy.pi / slices[0].wl
1728
+ Nslices = len(slices)
1729
+
1730
+ neesueps = numpy.zeros(Nslices, dtype=complex)
1731
+ nhh = numpy.zeros_like(neesueps)
1732
+ nRhe = numpy.zeros_like(neesueps)
1733
+ nRee = numpy.zeros_like(neesueps)
1734
+ nRhh = numpy.zeros_like(neesueps)
1735
+ nAC = numpy.zeros_like(neesueps)
1736
+
1737
+ M = Struct()
1738
+ M.ee = numpy.zeros((nmodi, nmodi, Nslices), dtype=complex)
1739
+ M.eesueps = numpy.zeros_like(M.ee)
1740
+ M.hh = numpy.zeros_like(M.ee)
1741
+ M.Rhe = numpy.zeros_like(M.ee)
1742
+
1743
+ for idx, slice in enumerate(slices):
1744
+
1745
+ for n in range(nmodi):
1746
+ Fhn = slice.modih[n]
1747
+ Fen = slice.modie[n]
1748
+ khidx = slice.modih[n].keff
1749
+
1750
+ for m in range(nmodi):
1751
+ Fem = slice.modie[m]
1752
+ Fhm = slice.modih[m]
1753
+ keidxp1 = slice.modie[m].keff
1754
+
1755
+ M.ee[n, m, idx] = scalarprod(Fen, Fem)
1756
+ M.eesueps[n, m, idx] = scalarprod(Fen, sueps(Fem))
1757
+ M.hh[n, m, idx] = scalarprod(Fhn, Fhm)
1758
+
1759
+ Fp1em = slice.modie[m]
1760
+ s1 = k0 * scalarprod(dot(Fhn), sueps(Fp1em)) / khidx**2
1761
+ s2 = k0 * scalarprod(Fhn, sueps(dot(Fp1em))) / keidxp1**2
1762
+ M.Rhe[n, m, idx] = (s1 + s2).item()
1763
+
1764
+ R = genera_rotazione(slices)
1765
+ Ident = numpy.eye(nmodi)
1766
+ for idx in range(Nslices):
1767
+ neesueps[idx] = numpy.linalg.norm(M.eesueps[:, :, idx] - Ident)
1768
+ nhh[idx] = numpy.linalg.norm(M.hh[:, :, idx] - Ident)
1769
+ nRhe[idx] = numpy.linalg.norm(M.Rhe[:, :, idx])
1770
+
1771
+ for idx in range(Nslices - 1):
1772
+ nRee[idx] = numpy.linalg.norm(
1773
+ numpy.dot(R.Ree[:, :, idx], R.Reem[:, :, idx]) - Ident
1774
+ )
1775
+ nRhh[idx] = numpy.linalg.norm(
1776
+ numpy.dot(R.Rhh[:, :, idx], R.Rhhm[:, :, idx]) - Ident
1777
+ )
1778
+ nAC[idx] = numpy.linalg.norm(
1779
+ numpy.dot(R.Rhe[:, :, idx], R.Reem[:, :, idx])
1780
+ + numpy.dot(R.Rhh[:, :, idx], R.Rhem[:, :, idx])
1781
+ )
1782
+
1783
+ ns1 = numpy.linalg.norm(numpy.r_[neesueps, nhh, nRhe])
1784
+ ns2 = numpy.linalg.norm(numpy.r_[nRee, nRhh, nAC])
1785
+ errore = numpy.linalg.norm(numpy.r_[ns1, ns2]) / scipy.sqrt(8 * nmodi)
1786
+ return errore
1787
+
1788
+
1789
+ def method_of_component(kz_, slices, Rot, uscelto=None, icomp=None):
1790
+
1791
+ kz = numpy.atleast_1d(kz_)
1792
+ abscomp = numpy.zeros(len(kz))
1793
+ # tmp = 500 # OKKIO: perche' 500?
1794
+ tmp = (
1795
+ 100 * len(slices[0].modie) * (len(slices) - 1)
1796
+ ) # OKKIO: dimension of Mvec * 50. enough?
1797
+ normu = numpy.zeros(tmp, dtype=complex)
1798
+ for m in range(len(kz)):
1799
+ M = Mvec(kz[m], slices, Rot)
1800
+ urn = numpy.zeros((M.shape[0], tmp), dtype=complex)
1801
+ if (uscelto is None) and (icomp is None):
1802
+ for k in range(tmp):
1803
+ numpy.random.seed()
1804
+ ur = numpy.random.rand(M.shape[0])
1805
+ urn[:, k] = ur / numpy.linalg.norm(ur)
1806
+ normu[k] = numpy.linalg.norm(numpy.dot(M, urn[:, k]))
1807
+ iurn = numpy.argmin(normu)
1808
+ uscelto = urn[:, iurn]
1809
+ icomp = numpy.argmax(uscelto)
1810
+
1811
+ Mmeno1u = numpy.linalg.solve(M, uscelto)
1812
+ abscomp[m] = 1.0 / numpy.linalg.norm(Mmeno1u)
1813
+
1814
+ return (abscomp, uscelto, icomp)
1815
+
1816
+
1817
+ def creaTeThSeSh(kz, slices):
1818
+
1819
+ Nslices = len(slices)
1820
+ nmodi = len(slices[0].modie)
1821
+ d = numpy.array([s.x2 - s.x1 for s in slices])
1822
+ k0 = 2.0 * numpy.pi / slices[0].wl
1823
+
1824
+ Th = numpy.zeros((nmodi, Nslices), dtype=complex)
1825
+ Sh = numpy.zeros_like(Th)
1826
+ Te = numpy.zeros_like(Th)
1827
+ Se = numpy.zeros_like(Th)
1828
+ Thleft = numpy.zeros_like(Th)
1829
+ Teleft = numpy.zeros_like(Th)
1830
+ Thright = numpy.zeros_like(Th)
1831
+ Teright = numpy.zeros_like(Th)
1832
+
1833
+ for idx in range(Nslices):
1834
+ ke = numpy.array([m.keff.item() for m in slices[idx].modie])
1835
+ kh = numpy.array([m.keff.item() for m in slices[idx].modih])
1836
+
1837
+ kxh = scipy.sqrt(kh**2 - kz**2)
1838
+ kxe = scipy.sqrt(ke**2 - kz**2)
1839
+
1840
+ Th[:, idx] = (k0 / kh) ** 2 * kxh / numpy.tan(kxh * d[idx])
1841
+ Sh[:, idx] = (k0 / kh) ** 2 * kxh / numpy.sin(kxh * d[idx])
1842
+ Te[:, idx] = (k0 / ke) ** 2 * kxe / numpy.tan(kxe * d[idx])
1843
+ Se[:, idx] = (k0 / ke) ** 2 * kxe / numpy.sin(kxe * d[idx])
1844
+
1845
+ ke = numpy.array([m.keff.item() for m in slices[0].modie])
1846
+ kh = numpy.array([m.keff.item() for m in slices[0].modih])
1847
+ kxh = scipy.sqrt(kh**2 - kz**2)
1848
+ kxe = scipy.sqrt(ke**2 - kz**2)
1849
+
1850
+ if slices[0].boundary.xleft == "Electric Wall":
1851
+ # ah = 0
1852
+ Thleft = -((k0 / kh) ** 2) * kxh * numpy.tan(kxh * d[0])
1853
+ Teleft = Te[:, 0]
1854
+ else:
1855
+ # ae = 0
1856
+ Teleft = -((k0 / ke) ** 2) * kxe * numpy.tan(kxe * d[0])
1857
+ Thleft = Th[:, 0]
1858
+
1859
+ ke = numpy.array([m.keff.item() for m in slices[-1].modie])
1860
+ kh = numpy.array([m.keff.item() for m in slices[-1].modih])
1861
+ kxh = scipy.sqrt(kh**2 - kz**2)
1862
+ kxe = scipy.sqrt(ke**2 - kz**2)
1863
+
1864
+ if slices[-1].boundary.xleft == "Electric Wall":
1865
+ # ah = 0
1866
+ Thright = -((k0 / kh) ** 2) * kxh * numpy.tan(kxh * d[-1])
1867
+ Teright = Te[:, -1]
1868
+ else:
1869
+ # ae = 0
1870
+ Teright = -((k0 / ke) ** 2) * kxe * numpy.tan(kxe * d[-1])
1871
+ Thright = Th[:, -1]
1872
+
1873
+ return (Te, Th, Se, Sh, Teleft, Teright, Thleft, Thright)
1874
+
1875
+
1876
+ def Mvec(kz, slices, R):
1877
+
1878
+ Nslices = len(slices)
1879
+ Rhh = R.Rhh
1880
+ Ree = R.Ree
1881
+ Rhe = R.Rhe
1882
+ Rhem = R.Rhem
1883
+ Rhhm = R.Rhhm
1884
+
1885
+ Te, Th, Se, Sh, Teleft, Teright, Thleft, Thright = creaTeThSeSh(kz, slices)
1886
+ Te[:, 0] = Teleft
1887
+ Te[:, -1] = Teright
1888
+ Th[:, 0] = Thleft
1889
+ Th[:, -1] = Thright
1890
+
1891
+ # case Nslices=2
1892
+ if Nslices == 2:
1893
+ raise NotImplementedError("2 slices not implemented yet.")
1894
+
1895
+ dim1 = Th.shape[0]
1896
+ M = numpy.zeros((2 * dim1 * (Nslices - 1), 2 * dim1 * (Nslices - 1)), dtype=complex)
1897
+ Dim1 = numpy.arange(dim1)
1898
+
1899
+ for idx in range(3, Nslices):
1900
+
1901
+ idxeJA = (2 * idx - 4) * dim1
1902
+ idxeJB = (2 * idx - 6) * dim1
1903
+ idxeJC = (2 * idx - 2) * dim1
1904
+ idxeJD = (2 * idx - 3) * dim1
1905
+
1906
+ idxeIA = (2 * idx - 4) * dim1
1907
+ idxeIB = (2 * idx - 4) * dim1
1908
+ idxeIC = (2 * idx - 4) * dim1
1909
+ idxeID = (2 * idx - 4) * dim1
1910
+
1911
+ idxhJA = (2 * idx - 3) * dim1
1912
+ idxhJB = (2 * idx - 5) * dim1
1913
+ idxhJC = (2 * idx - 1) * dim1
1914
+ idxhJD = (2 * idx - 4) * dim1
1915
+
1916
+ idxhIA = (2 * idx - 3) * dim1
1917
+ idxhIB = (2 * idx - 3) * dim1
1918
+ idxhIC = (2 * idx - 3) * dim1
1919
+ idxhID = (2 * idx - 3) * dim1
1920
+
1921
+ IAe = Dim1 + idxeIA
1922
+ JAe = Dim1 + idxeJA
1923
+ IBe = Dim1 + idxeIB
1924
+ JBe = Dim1 + idxeJB
1925
+ ICe = Dim1 + idxeIC
1926
+ JCe = Dim1 + idxeJC
1927
+ IDe = Dim1 + idxeID
1928
+ JDe = Dim1 + idxeJD
1929
+
1930
+ IAh = Dim1 + idxhIA
1931
+ JAh = Dim1 + idxhJA
1932
+ IBh = Dim1 + idxhIB
1933
+ JBh = Dim1 + idxhJB
1934
+ ICh = Dim1 + idxhIC
1935
+ JCh = Dim1 + idxhJC
1936
+ IDh = Dim1 + idxhID
1937
+ JDh = Dim1 + idxhJD
1938
+
1939
+ M[numpy.ix_(IAe, JAe)] = numpy.dot(
1940
+ Ree[:, :, idx - 1].T * Te[:, idx - 1], Ree[:, :, idx - 1]
1941
+ ) + numpy.diag(Te[:, idx])
1942
+ M[numpy.ix_(IBe, JBe)] = -Ree[:, :, idx - 1].T * Se[:, idx - 1]
1943
+ M[numpy.ix_(ICe, JCe)] = -Se[:, numpy.newaxis, idx] * Ree[:, :, idx]
1944
+ M[numpy.ix_(IDe, JDe)] = -kz * numpy.dot(
1945
+ Ree[:, :, idx - 1].T, Rhem[:, :, idx - 1].T
1946
+ )
1947
+
1948
+ M[numpy.ix_(IAh, JAh)] = numpy.dot(
1949
+ Rhhm[:, :, idx - 1] * Th[:, idx - 1], Rhh[:, :, idx - 1]
1950
+ ) + numpy.diag(Th[:, idx])
1951
+ M[numpy.ix_(IBh, JBh)] = -Rhhm[:, :, idx - 1] * Sh[:, idx - 1]
1952
+ M[numpy.ix_(ICh, JCh)] = -Sh[:, numpy.newaxis, idx] * Rhh[:, :, idx]
1953
+ M[numpy.ix_(IDh, JDh)] = kz * numpy.dot(Rhhm[:, :, idx - 1], Rhe[:, :, idx - 1])
1954
+
1955
+ idx = 2
1956
+
1957
+ idxeJA = (2 * idx - 4) * dim1
1958
+ idxeJC = (2 * idx - 2) * dim1
1959
+ idxeJD = (2 * idx - 3) * dim1
1960
+
1961
+ idxeIA = (2 * idx - 4) * dim1
1962
+ idxeIC = (2 * idx - 4) * dim1
1963
+ idxeID = (2 * idx - 4) * dim1
1964
+
1965
+ idxhJA = (2 * idx - 3) * dim1
1966
+ idxhJC = (2 * idx - 1) * dim1
1967
+ idxhJD = (2 * idx - 4) * dim1
1968
+
1969
+ idxhIA = (2 * idx - 3) * dim1
1970
+ idxhIC = (2 * idx - 3) * dim1
1971
+ idxhID = (2 * idx - 3) * dim1
1972
+
1973
+ IAe = Dim1 + idxeIA
1974
+ JAe = Dim1 + idxeJA
1975
+ ICe = Dim1 + idxeIC
1976
+ JCe = Dim1 + idxeJC
1977
+ IDe = Dim1 + idxeID
1978
+ JDe = Dim1 + idxeJD
1979
+
1980
+ IAh = Dim1 + idxhIA
1981
+ JAh = Dim1 + idxhJA
1982
+ ICh = Dim1 + idxhIC
1983
+ JCh = Dim1 + idxhJC
1984
+ IDh = Dim1 + idxhID
1985
+ JDh = Dim1 + idxhJD
1986
+
1987
+ idx -= 1
1988
+
1989
+ M[numpy.ix_(IAe, JAe)] = numpy.dot(
1990
+ Ree[:, :, idx - 1].T * Te[:, idx - 1], Ree[:, :, idx - 1]
1991
+ ) + numpy.diag(Te[:, idx])
1992
+ M[numpy.ix_(ICe, JCe)] = -Se[:, numpy.newaxis, idx] * Ree[:, :, idx]
1993
+ M[numpy.ix_(IDe, JDe)] = -kz * numpy.dot(
1994
+ Ree[:, :, idx - 1].T, Rhem[:, :, idx - 1].T
1995
+ )
1996
+
1997
+ M[numpy.ix_(IAh, JAh)] = numpy.dot(
1998
+ Rhhm[:, :, idx - 1] * Th[:, idx - 1], Rhh[:, :, idx - 1]
1999
+ ) + numpy.diag(Th[:, idx])
2000
+ M[numpy.ix_(ICh, JCh)] = -Sh[:, numpy.newaxis, idx] * Rhh[:, :, idx]
2001
+ M[numpy.ix_(IDh, JDh)] = kz * numpy.dot(Rhhm[:, :, idx - 1], Rhe[:, :, idx - 1])
2002
+
2003
+ idx = Nslices
2004
+
2005
+ idxeJA = (2 * idx - 4) * dim1
2006
+ idxeJB = (2 * idx - 6) * dim1
2007
+ idxeJD = (2 * idx - 3) * dim1
2008
+
2009
+ idxeIA = (2 * idx - 4) * dim1
2010
+ idxeIB = (2 * idx - 4) * dim1
2011
+ idxeID = (2 * idx - 4) * dim1
2012
+
2013
+ idxhJA = (2 * idx - 3) * dim1
2014
+ idxhJB = (2 * idx - 5) * dim1
2015
+ idxhJD = (2 * idx - 4) * dim1
2016
+
2017
+ idxhIA = (2 * idx - 3) * dim1
2018
+ idxhIB = (2 * idx - 3) * dim1
2019
+ idxhID = (2 * idx - 3) * dim1
2020
+
2021
+ IAe = Dim1 + idxeIA
2022
+ JAe = Dim1 + idxeJA
2023
+ IBe = Dim1 + idxeIB
2024
+ JBe = Dim1 + idxeJB
2025
+ IDe = Dim1 + idxeID
2026
+ JDe = Dim1 + idxeJD
2027
+
2028
+ IAh = Dim1 + idxhIA
2029
+ JAh = Dim1 + idxhJA
2030
+ IBh = Dim1 + idxhIB
2031
+ JBh = Dim1 + idxhJB
2032
+ IDh = Dim1 + idxhID
2033
+ JDh = Dim1 + idxhJD
2034
+
2035
+ idx -= 1
2036
+
2037
+ M[numpy.ix_(IAe, JAe)] = numpy.dot(
2038
+ Ree[:, :, idx - 1].T * Te[:, idx - 1], Ree[:, :, idx - 1]
2039
+ ) + numpy.diag(Te[:, idx])
2040
+ M[numpy.ix_(IBe, JBe)] = -Ree[:, :, idx - 1].T * Se[:, idx - 1]
2041
+ M[numpy.ix_(IDe, JDe)] = -kz * numpy.dot(
2042
+ Ree[:, :, idx - 1].T, Rhem[:, :, idx - 1].T
2043
+ )
2044
+
2045
+ M[numpy.ix_(IAh, JAh)] = numpy.dot(
2046
+ Rhhm[:, :, idx - 1] * Th[:, idx - 1], Rhh[:, :, idx - 1]
2047
+ ) + numpy.diag(Th[:, idx])
2048
+ M[numpy.ix_(IBh, JBh)] = -Rhhm[:, :, idx - 1] * Sh[:, idx - 1]
2049
+ M[numpy.ix_(IDh, JDh)] = kz * numpy.dot(Rhhm[:, :, idx - 1], Rhe[:, :, idx - 1])
2050
+
2051
+ return M
2052
+
2053
+
2054
+ def check_matching(kz, slices, modo, R):
2055
+
2056
+ Te, Th, Se, Sh, Teleft, Teright, Thleft, Thright = creaTeThSeSh(kz, slices)
2057
+ Shr = numpy.array([m.sr for m in modo.modih])
2058
+ Shl = numpy.array([m.sl for m in modo.modih])
2059
+ Ser = numpy.array([m.sr for m in modo.modie])
2060
+ Sel = numpy.array([m.sl for m in modo.modie])
2061
+ Ahr = numpy.array([m.ar for m in modo.modih])
2062
+ Ahl = numpy.array([m.al for m in modo.modih])
2063
+ Aer = numpy.array([m.ar for m in modo.modie])
2064
+ Ael = numpy.array([m.al for m in modo.modie])
2065
+
2066
+ kz = modo.keff
2067
+
2068
+ n1 = numpy.linalg.norm(numpy.dot(R.Rhh[:, :, 1], Shl[:, 2]) - Shr[:, 1])
2069
+ n2 = numpy.linalg.norm(numpy.dot(R.Rhh[:, :, 0], Shl[:, 1]) - Shr[:, 0])
2070
+ n3 = numpy.linalg.norm(numpy.dot(R.Ree[:, :, 1], Sel[:, 2]) - Ser[:, 1])
2071
+ n4 = numpy.linalg.norm(numpy.dot(R.Ree[:, :, 0], Sel[:, 1]) - Ser[:, 0])
2072
+ n5 = numpy.linalg.norm(
2073
+ numpy.dot(R.Rhh[:, :, 1], Ahl[:, 2])
2074
+ - kz * numpy.dot(R.Rhe[:, :, 1], Sel[:, 2])
2075
+ - Ahr[:, 1]
2076
+ )
2077
+ n6 = numpy.linalg.norm(
2078
+ numpy.dot(R.Rhh[:, :, 0], Ahl[:, 1])
2079
+ - kz * numpy.dot(R.Rhe[:, :, 0], Sel[:, 1])
2080
+ - Ahr[:, 0]
2081
+ )
2082
+ n7 = numpy.linalg.norm(
2083
+ numpy.dot(R.Reem[:, :, 1].T, Ael[:, 2])
2084
+ + kz * numpy.dot(R.Rhem[:, :, 1].T, Shl[:, 2])
2085
+ - Aer[:, 1]
2086
+ )
2087
+ n8 = numpy.linalg.norm(
2088
+ numpy.dot(R.Reem[:, :, 0].T, Ael[:, 1])
2089
+ + kz * numpy.dot(R.Rhem[:, :, 0].T, Shl[:, 1])
2090
+ - Aer[:, 0]
2091
+ )
2092
+ n9 = numpy.linalg.norm(-Te[:, 0] * Sel[:, 0] + Se[:, 0] * Ser[:, 0] - Ael[:, 0])
2093
+ n10 = numpy.linalg.norm(-Te[:, 1] * Sel[:, 1] + Se[:, 1] * Ser[:, 1] - Ael[:, 1])
2094
+ n11 = numpy.linalg.norm(-Te[:, 2] * Sel[:, 2] + Se[:, 2] * Ser[:, 2] - Ael[:, 2])
2095
+ n12 = numpy.linalg.norm(-Th[:, 0] * Shl[:, 0] + Sh[:, 0] * Shr[:, 0] - Ahl[:, 0])
2096
+ n13 = numpy.linalg.norm(-Th[:, 1] * Shl[:, 1] + Sh[:, 1] * Shr[:, 1] - Ahl[:, 1])
2097
+ n14 = numpy.linalg.norm(-Th[:, 2] * Shl[:, 2] + Sh[:, 2] * Shr[:, 2] - Ahl[:, 2])
2098
+ n15 = numpy.linalg.norm(-Sh[:, 0] * Shl[:, 0] + Th[:, 0] * Shr[:, 0] - Ahr[:, 0])
2099
+ n16 = numpy.linalg.norm(-Sh[:, 1] * Shl[:, 1] + Th[:, 1] * Shr[:, 1] - Ahr[:, 1])
2100
+ n17 = numpy.linalg.norm(-Sh[:, 2] * Shl[:, 2] + Th[:, 2] * Shr[:, 2] - Ahr[:, 2])
2101
+ n18 = numpy.linalg.norm(-Se[:, 0] * Sel[:, 0] + Te[:, 0] * Ser[:, 0] - Aer[:, 0])
2102
+ n19 = numpy.linalg.norm(-Se[:, 1] * Sel[:, 1] + Te[:, 1] * Ser[:, 1] - Aer[:, 1])
2103
+ n20 = numpy.linalg.norm(-Se[:, 2] * Sel[:, 2] + Te[:, 2] * Ser[:, 2] - Aer[:, 2])
2104
+
2105
+ Nv = numpy.array(
2106
+ [
2107
+ n1,
2108
+ n2,
2109
+ n3,
2110
+ n4,
2111
+ n5,
2112
+ n6,
2113
+ n7,
2114
+ n8,
2115
+ n9,
2116
+ n10,
2117
+ n11,
2118
+ n12,
2119
+ n13,
2120
+ n14,
2121
+ n15,
2122
+ n16,
2123
+ n17,
2124
+ n18,
2125
+ n19,
2126
+ n20,
2127
+ ]
2128
+ )
2129
+ N = numpy.linalg.norm(Nv)
2130
+ return N
2131
+
2132
+
2133
+ def creacoeffx3(kz, solution, slices, R):
2134
+
2135
+ Nslices = len(slices)
2136
+
2137
+ xl = slices[0].boundary.xleft
2138
+ xr = slices[0].boundary.xright
2139
+
2140
+ nmodi = len(slices[0].modih)
2141
+ Rhh = R.Rhh
2142
+ Ree = R.Ree
2143
+ Rhe = R.Rhe
2144
+ Rhem = R.Rhem
2145
+ Reem = R.Reem
2146
+
2147
+ Te, Th, Se, Sh, Teleft, Teright, Thleft, Thright = creaTeThSeSh(kz, slices)
2148
+
2149
+ # sl2end = numpy.reshape(solution, (nmodi, 2 * (Nslices - 1)))
2150
+ sl2end = numpy.reshape(solution, (2 * (Nslices - 1), nmodi)).T
2151
+ idxslices = 2 * numpy.arange((Nslices - 1))
2152
+ sle2end = sl2end[:, idxslices]
2153
+ slh2end = sl2end[:, idxslices + 1]
2154
+
2155
+ ale = numpy.zeros((nmodi, Nslices), dtype=complex)
2156
+ sre = numpy.zeros_like(ale)
2157
+ are = numpy.zeros_like(ale)
2158
+ alh = numpy.zeros_like(ale)
2159
+ srh = numpy.zeros_like(ale)
2160
+ arh = numpy.zeros_like(ale)
2161
+
2162
+ if (xl == "Electric Wall") & (xr == "Electric Wall"):
2163
+
2164
+ sle = numpy.c_[numpy.zeros((nmodi, 1)), sle2end]
2165
+ sre[:, -1] = numpy.zeros(nmodi)
2166
+ are[:, -1] = -Se[:, -1] * sle[:, -1]
2167
+ ale[:, -1] = -Te[:, -1] * sle[:, -1]
2168
+
2169
+ slh = numpy.c_[numpy.zeros((nmodi, 1)), slh2end]
2170
+ arh[:, -1] = numpy.zeros(nmodi)
2171
+ srh[:, -1] = Sh[:, -1] / Th[:, -1] * slh[:, -1]
2172
+ alh[:, -1] = -Th[:, -1] * slh[:, -1] + Sh[:, -1] * srh[:, -1]
2173
+
2174
+ for idx in range(1, Nslices):
2175
+ sre[:, idx - 1] = numpy.dot(Ree[:, :, idx - 1], sle[:, idx])
2176
+ srh[:, idx - 1] = numpy.dot(Rhh[:, :, idx - 1], slh[:, idx])
2177
+
2178
+ slh[:, 0] = Sh[:, 0] / Th[:, 0] * srh[:, 0]
2179
+
2180
+ for idx in range(Nslices - 1, 0, -1):
2181
+ are[:, idx - 1] = numpy.dot(
2182
+ Reem[:, :, idx - 1].T, ale[:, idx]
2183
+ ) + kz * numpy.dot(Rhem[:, :, idx - 1].T, slh[:, idx])
2184
+ arh[:, idx - 1] = numpy.dot(
2185
+ Rhh[:, :, idx - 1], alh[:, idx]
2186
+ ) - kz * numpy.dot(Rhe[:, :, idx - 1], sle[:, idx])
2187
+ ale[:, idx - 1] = (
2188
+ -Te[:, idx - 1] * sle[:, idx - 1] + Se[:, idx - 1] * sre[:, idx - 1]
2189
+ )
2190
+ alh[:, idx - 1] = (
2191
+ -Th[:, idx - 1] * slh[:, idx - 1] + Sh[:, idx - 1] * srh[:, idx - 1]
2192
+ )
2193
+
2194
+ elif (xl == "Electric Wall") & (xr == "Magnetic Wall"):
2195
+
2196
+ sle = numpy.c_[numpy.zeros((nmodi, 1)), sle2end]
2197
+ are[:, -1] = numpy.zeros(nmodi)
2198
+ sre[:, -1] = Se[:, -1] / Te[:, -1] * sle[:, -1]
2199
+ ale[:, -1] = -Te[:, -1] * sle[:, -1] + Se[:, -1] * sre[:, -1]
2200
+
2201
+ slh = numpy.c_[numpy.zeros((nmodi, 1)), slh2end]
2202
+ srh[:, -1] = numpy.zeros(nmodi)
2203
+ arh[:, -1] = -Sh[:, -1] * slh[:, -1]
2204
+ alh[:, -1] = -Th[:, -1] * slh[:, -1]
2205
+
2206
+ for idx in range(1, Nslices):
2207
+ sre[:, idx - 1] = numpy.dot(Ree[:, :, idx - 1], sle[:, idx])
2208
+ srh[:, idx - 1] = numpy.dot(Rhh[:, :, idx - 1], slh[:, idx])
2209
+
2210
+ slh[:, 0] = Sh[:, 0] / Th[:, 0] * srh[:, 0]
2211
+
2212
+ for idx in range(Nslices - 1, 0, -1):
2213
+ are[:, idx - 1] = numpy.dot(
2214
+ Reem[:, :, idx - 1].T, ale[:, idx]
2215
+ ) + kz * numpy.dot(Rhem[:, :, idx - 1].T, slh[:, idx])
2216
+ arh[:, idx - 1] = numpy.dot(
2217
+ Rhh[:, :, idx - 1], alh[:, idx]
2218
+ ) - kz * numpy.dot(Rhe[:, :, idx - 1], sle[:, idx])
2219
+ ale[:, idx - 1] = (
2220
+ -Te[:, idx - 1] * sle[:, idx - 1] + Se[:, idx - 1] * sre[:, idx - 1]
2221
+ )
2222
+ alh[:, idx - 1] = (
2223
+ -Th[:, idx - 1] * slh[:, idx - 1] + Sh[:, idx - 1] * srh[:, idx - 1]
2224
+ )
2225
+
2226
+ elif (xl == "Magnetic Wall") & (xr == "Electric Wall"):
2227
+
2228
+ sle = numpy.c_[numpy.zeros((nmodi, 1)), sle2end]
2229
+ slh = numpy.c_[numpy.zeros((nmodi, 1)), slh2end]
2230
+
2231
+ sre[:, -1] = numpy.zeros(nmodi)
2232
+ arh[:, -1] = numpy.zeros(nmodi)
2233
+ srh[:, -1] = Sh[:, -1] / Th[:, -1] * slh[:, -1]
2234
+ are[:, -1] = -Se[:, -1] * sle[:, -1]
2235
+ ale[:, -1] = -Te[:, -1] * sle[:, -1]
2236
+ alh[:, -1] = -Th[:, -1] * slh[:, -1] + Sh[:, -1] * srh[:, -1]
2237
+
2238
+ for idx in range(1, Nslices):
2239
+ sre[:, idx - 1] = numpy.dot(Ree[:, :, idx - 1], sle[:, idx])
2240
+ srh[:, idx - 1] = numpy.dot(Rhh[:, :, idx - 1], slh[:, idx])
2241
+
2242
+ sle[:, 0] = Se[:, 0] / Te[:, 0] * sre[:, 0]
2243
+
2244
+ for idx in range(Nslices - 1, 0, -1):
2245
+ are[:, idx - 1] = numpy.dot(
2246
+ Reem[:, :, idx - 1].T, ale[:, idx]
2247
+ ) + kz * numpy.dot(Rhem[:, :, idx - 1].T, slh[:, idx])
2248
+ arh[:, idx - 1] = numpy.dot(
2249
+ Rhh[:, :, idx - 1], alh[:, idx]
2250
+ ) - kz * numpy.dot(Rhe[:, :, idx - 1], sle[:, idx])
2251
+ ale[:, idx - 1] = (
2252
+ -Te[:, idx - 1] * sle[:, idx - 1] + Se[:, idx - 1] * sre[:, idx - 1]
2253
+ )
2254
+ alh[:, idx - 1] = (
2255
+ -Th[:, idx - 1] * slh[:, idx - 1] + Sh[:, idx - 1] * srh[:, idx - 1]
2256
+ )
2257
+
2258
+ elif (xl == "Magnetic Wall") & (xr == "Magnetic Wall"):
2259
+
2260
+ sle = numpy.c_[numpy.zeros((nmodi, 1)), sle2end]
2261
+ slh = numpy.c_[numpy.zeros((nmodi, 1)), slh2end]
2262
+
2263
+ srh[:, -1] = numpy.zeros(nmodi)
2264
+ are[:, -1] = numpy.zeros(nmodi)
2265
+ sre[:, -1] = Se[:, -1] / Te[:, -1] * sle[:, -1]
2266
+ arh[:, -1] = -Sh[:, -1] * slh[:, -1]
2267
+ alh[:, -1] = -Th[:, -1] * slh[:, -1]
2268
+ ale[:, -1] = -Te[:, -1] * sle[:, -1] + Se[:, -1] * sre[:, -1]
2269
+
2270
+ for idx in range(1, Nslices):
2271
+ sre[:, idx - 1] = numpy.dot(Ree[:, :, idx - 1], sle[:, idx])
2272
+ srh[:, idx - 1] = numpy.dot(Rhh[:, :, idx - 1], slh[:, idx])
2273
+
2274
+ sle[:, 0] = Se[:, 0] / Te[:, 0] * sre[:, 0]
2275
+
2276
+ for idx in range(Nslices - 1, 0, -1):
2277
+ are[:, idx - 1] = numpy.dot(
2278
+ Reem[:, :, idx - 1].T, ale[:, idx]
2279
+ ) + kz * numpy.dot(Rhem[:, :, idx - 1].T, slh[:, idx])
2280
+ arh[:, idx - 1] = numpy.dot(
2281
+ Rhh[:, :, idx - 1], alh[:, idx]
2282
+ ) - kz * numpy.dot(Rhe[:, :, idx - 1], sle[:, idx])
2283
+ ale[:, idx - 1] = (
2284
+ -Te[:, idx - 1] * sle[:, idx - 1] + Se[:, idx - 1] * sre[:, idx - 1]
2285
+ )
2286
+ alh[:, idx - 1] = (
2287
+ -Th[:, idx - 1] * slh[:, idx - 1] + Sh[:, idx - 1] * srh[:, idx - 1]
2288
+ )
2289
+
2290
+ else:
2291
+ raise ValueError("Unrecognized boundary condition")
2292
+
2293
+ modie = []
2294
+ modih = []
2295
+ kh = numpy.zeros((nmodi, Nslices), dtype=complex)
2296
+ ke = numpy.zeros_like(kh)
2297
+
2298
+ for n in range(nmodi):
2299
+
2300
+ for q, slice in enumerate(slices):
2301
+ kh[n, q] = slice.modih[n].keff.item()
2302
+ ke[n, q] = slice.modie[n].keff.item()
2303
+
2304
+ modoe = FMMMode1dx()
2305
+ modoe.sl = sle[n, :]
2306
+ modoe.al = ale[n, :]
2307
+ modoe.sr = sre[n, :]
2308
+ modoe.ar = are[n, :]
2309
+ modoe.U = slices[0].Ux
2310
+ modoe.k = ke[n, :]
2311
+ modie.append(modoe)
2312
+
2313
+ modoh = FMMMode1dx()
2314
+ modoh.sl = slh[n, :]
2315
+ modoh.al = alh[n, :]
2316
+ modoh.sr = srh[n, :]
2317
+ modoh.ar = arh[n, :]
2318
+ modoh.U = slices[0].Ux
2319
+ modoh.k = kh[n, :]
2320
+ modih.append(modoh)
2321
+
2322
+ return (modie, modih)
2323
+
2324
+
2325
+ def FMM1d_x_component(slices, nmodi, verbosity=0):
2326
+
2327
+ Message("Matching 1dmodes on slices interfaces.", 1).show(verbosity)
2328
+
2329
+ TolnullEig = 1e-1
2330
+ searchinterval = 6e-3
2331
+
2332
+ # Ux = slices[0].Ux
2333
+ lambda0 = slices[0].wl
2334
+ k0 = 2.0 * numpy.pi / lambda0
2335
+
2336
+ Rot = genera_rotazione(slices)
2337
+
2338
+ maxrekz = -1e6
2339
+ minrekz = 1e6
2340
+
2341
+ for slice in slices:
2342
+ maxrekz0 = numpy.max(
2343
+ numpy.real([m.keff for m in slice.modih] + [m.keff for m in slice.modie])
2344
+ )
2345
+ minrekz0 = numpy.min(numpy.real(k0 * slice.refractiveindex))
2346
+ if maxrekz < maxrekz0:
2347
+ maxrekz = maxrekz0
2348
+ if minrekz > minrekz0:
2349
+ minrekz = minrekz0
2350
+
2351
+ # only guided modes
2352
+ # rekz = numpy.linspace(maxrekz + searchinterval, minrekz - searchinterval, numpy.floor(3. / searchinterval))
2353
+ # also radiative modes
2354
+ # increase 10./searchinterval if not enough candidate zeros are found
2355
+ # rekz = numpy.linspace(maxrekz + searchinterval, 0.,
2356
+ # numpy.floor(6. / searchinterval *
2357
+ # (1 + (minrekz - searchinterval) / (maxrekz - minrekz + 2 * searchinterval))))
2358
+ # make rekz[1]-rekz[0] <= searchinterval
2359
+ # OKKIO: forse mettere 3x punti??
2360
+ rekz = numpy.linspace(
2361
+ maxrekz + searchinterval,
2362
+ 0.0,
2363
+ 10.0 * numpy.floor((maxrekz + searchinterval) / searchinterval),
2364
+ )
2365
+
2366
+ abscomp, uscelto, icomp = method_of_component(maxrekz, slices, Rot)
2367
+ matchingre = method_of_component(rekz, slices, Rot, uscelto, icomp)[0]
2368
+ nre = rekz / k0
2369
+ zerire = findzerosnew(nre, numpy.abs(matchingre), searchinterval)[0]
2370
+ kz1 = k0 * zerire - searchinterval / 2.0 + 1j * searchinterval
2371
+ kz2 = k0 * zerire + searchinterval / 2.0 - 1j * searchinterval
2372
+ Message("Found %d possible zeros." % len(zerire), 1).show(verbosity)
2373
+ Message("Possible zeros:\n%s" % zerire, 3).show(verbosity)
2374
+ Message("Refining zeros.", 1).show(verbosity)
2375
+ mk = 0
2376
+ modi = []
2377
+ for m in range(len(kz1)):
2378
+
2379
+ if mk == nmodi:
2380
+ break
2381
+
2382
+ abscomp, uscelto, icomp = method_of_component(
2383
+ numpy.mean([kz1[m], kz2[m]]), slices, Rot
2384
+ )
2385
+ z0, valf = fzzeroabs2(
2386
+ lambda kz: method_of_component(kz, slices, Rot, uscelto, icomp)[0],
2387
+ kz1[m],
2388
+ kz2[m],
2389
+ )
2390
+
2391
+ if len(valf) > 0:
2392
+
2393
+ eigval, eigvec = numpy.linalg.eig(Mvec(z0, slices, Rot))
2394
+ mk += 1
2395
+ index0 = numpy.where(numpy.abs(eigval) < TolnullEig)[0]
2396
+ if len(index0) > 1:
2397
+ Message(
2398
+ "Two eigenvectors found with the same eigenvalue. Mixing of modes may result",
2399
+ 1,
2400
+ ).show(verbosity)
2401
+
2402
+ imm = numpy.argmin(numpy.abs(eigval))
2403
+ solution = eigvec[:, imm]
2404
+ modie, modih = creacoeffx3(z0, solution, slices, Rot)
2405
+ modo = FMMMode2d()
2406
+ modo.keff = z0
2407
+ modo.neff = modo.keff * lambda0 / (2 * numpy.pi)
2408
+ modo.modie = modie
2409
+ modo.modih = modih
2410
+ modo.zero = numpy.linalg.norm(numpy.dot(Mvec(z0, slices, Rot), solution))
2411
+ modo.distance_from_zero = check_matching(z0, slices, modo, Rot)
2412
+ modo.slicesx = slices
2413
+ modi.append(modo)
2414
+
2415
+ Message(
2416
+ "Distance from zero of the final solution: %f"
2417
+ % modo.distance_from_zero,
2418
+ 2,
2419
+ ).show(verbosity)
2420
+ Message("%d. neff = %s" % (mk, modo.keff / k0), 1).show(verbosity)
2421
+
2422
+ return modi