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/modesolvers/FD.py ADDED
@@ -0,0 +1,2536 @@
1
+ # pylint: disable=line-too-long,too-many-locals,too-many-statements,too-many-branches
2
+ # pylint: disable=redefined-builtin,wildcard-import,unused-wildcard-import
3
+ # pylint: disable=attribute-defined-outside-init,too-many-instance-attributes
4
+ # pylint: disable=arguments-differ,too-many-arguments
5
+ """Finite Difference Modesolver.
6
+
7
+ @see: Fallahkhair, "Vector Finite Difference Modesolver for Anisotropic Dielectric Waveguides",
8
+ @see: JLT 2007 <http://www.photonics.umd.edu/wp-content/uploads/pubs/ja-20/Fallahkhair_JLT_26_1423_2008.pdf>}
9
+ @see: DOI of above reference <http://doi.org/10.1109/JLT.2008.923643>
10
+ @see: http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=12734&objectType=FILE
11
+
12
+ """
13
+ import numpy
14
+ import scipy
15
+ import scipy.optimize
16
+
17
+ import EMpy.utils
18
+ from EMpy.modesolvers.interface import Mode, ModeSolver
19
+
20
+
21
+ class SVFDModeSolver(ModeSolver):
22
+
23
+ """
24
+ This function calculates the modes of a dielectric waveguide
25
+ using the semivectorial finite difference method.
26
+ It is slightly faster than the full-vectorial VFDModeSolver,
27
+ but it does not accept non-isotropic permittivity. For example,
28
+ birefringent materials, which have
29
+ different refractive indices along different dimensions cannot be used.
30
+ It is adapted from the "svmodes.m" matlab code of Thomas Murphy and co-workers.
31
+ https://www.mathworks.com/matlabcentral/fileexchange/12734-waveguide-mode-solver/content/svmodes.m
32
+
33
+ Parameters
34
+ ----------
35
+ wl : float
36
+ optical wavelength
37
+ units are arbitrary, but must be self-consistent.
38
+ I.e., just use micron for everything.
39
+ x : 1D array of floats
40
+ Array of x-values
41
+ y : 1D array of floats
42
+ Array of y-values
43
+ epsfunc : function
44
+ This is a function that provides the relative permittivity matrix
45
+ (square of the refractive index) as a function of its x and y
46
+ numpy.arrays (the function's input parameters). The function must be
47
+ of the form: ``myRelativePermittivity(x,y)``, where x and y are 2D
48
+ numpy "meshgrid" arrays that will be passed by this function.
49
+ The function returns a relative permittivity numpy.array of
50
+ shape( x.shape[0], y.shape[0] ) where each element of the array
51
+ is a single float, corresponding the an isotropic refractive index.
52
+ If an anisotropic refractive index is desired, the full-vectorial
53
+ VFDModeSolver function should be used.
54
+ boundary : str
55
+ This is a string that identifies the type of boundary conditions applied.
56
+ The following options are available:
57
+ 'A' - Hx is antisymmetric, Hy is symmetric.
58
+ 'S' - Hx is symmetric and, Hy is antisymmetric.
59
+ '0' - Hx and Hy are zero immediately outside of the boundary.
60
+ The string identifies all four boundary conditions, in the order:
61
+ North, south, east, west.
62
+ For example, boundary='000A'
63
+
64
+ method : str
65
+ must be 'Ex', 'Ey', or 'scalar'
66
+ this identifies the field that will be calculated.
67
+
68
+
69
+ Returns
70
+ -------
71
+ self : an instance of the SVFDModeSolver class
72
+ Typically self.solve() will be called in order to actually find the modes.
73
+
74
+ """
75
+
76
+ def __init__(self, wl, x, y, epsfunc, boundary, method="Ex"):
77
+ self.wl = wl
78
+ self.x = x
79
+ self.y = y
80
+ self.epsfunc = epsfunc
81
+ self.boundary = boundary
82
+ self.method = method
83
+
84
+ def _get_eps(self, xc, yc):
85
+ eps = self.epsfunc(xc, yc)
86
+ eps = numpy.c_[eps[:, 0:1], eps, eps[:, -1:]]
87
+ eps = numpy.r_[eps[0:1, :], eps, eps[-1:, :]]
88
+ return eps
89
+
90
+ def build_matrix(self):
91
+
92
+ from scipy.sparse import coo_matrix
93
+
94
+ wl = self.wl
95
+ x = self.x
96
+ y = self.y
97
+ boundary = self.boundary
98
+ method = self.method
99
+
100
+ dx = numpy.diff(x)
101
+ dy = numpy.diff(y)
102
+
103
+ dx = numpy.r_[dx[0], dx, dx[-1]].reshape(-1, 1)
104
+ dy = numpy.r_[dy[0], dy, dy[-1]].reshape(1, -1)
105
+
106
+ xc = (x[:-1] + x[1:]) / 2
107
+ yc = (y[:-1] + y[1:]) / 2
108
+ eps = self._get_eps(xc, yc)
109
+
110
+ nx = len(xc)
111
+ ny = len(yc)
112
+
113
+ self.nx = nx
114
+ self.ny = ny
115
+
116
+ k = 2 * numpy.pi / wl
117
+
118
+ ones_nx = numpy.ones((nx, 1))
119
+ ones_ny = numpy.ones((1, ny))
120
+
121
+ n = numpy.dot(ones_nx, 0.5 * (dy[:, 2:] + dy[:, 1:-1])).flatten()
122
+ s = numpy.dot(ones_nx, 0.5 * (dy[:, 0:-2] + dy[:, 1:-1])).flatten()
123
+ e = numpy.dot(0.5 * (dx[2:, :] + dx[1:-1, :]), ones_ny).flatten()
124
+ w = numpy.dot(0.5 * (dx[0:-2, :] + dx[1:-1, :]), ones_ny).flatten()
125
+ p = numpy.dot(dx[1:-1, :], ones_ny).flatten()
126
+ q = numpy.dot(ones_nx, dy[:, 1:-1]).flatten()
127
+
128
+ en = eps[1:-1, 2:].flatten()
129
+ es = eps[1:-1, 0:-2].flatten()
130
+ ee = eps[2:, 1:-1].flatten()
131
+ ew = eps[0:-2, 1:-1].flatten()
132
+ ep = eps[1:-1, 1:-1].flatten()
133
+
134
+ # three methods: Ex, Ey and scalar
135
+
136
+ if method == "Ex":
137
+
138
+ # Ex
139
+
140
+ An = 2 / n / (n + s)
141
+ As = 2 / s / (n + s)
142
+ Ae = (
143
+ 8
144
+ * (p * (ep - ew) + 2 * w * ew)
145
+ * ee
146
+ / (
147
+ (p * (ep - ee) + 2 * e * ee)
148
+ * (p**2 * (ep - ew) + 4 * w**2 * ew)
149
+ + (p * (ep - ew) + 2 * w * ew)
150
+ * (p**2 * (ep - ee) + 4 * e**2 * ee)
151
+ )
152
+ )
153
+ Aw = (
154
+ 8
155
+ * (p * (ep - ee) + 2 * e * ee)
156
+ * ew
157
+ / (
158
+ (p * (ep - ee) + 2 * e * ee)
159
+ * (p**2 * (ep - ew) + 4 * w**2 * ew)
160
+ + (p * (ep - ew) + 2 * w * ew)
161
+ * (p**2 * (ep - ee) + 4 * e**2 * ee)
162
+ )
163
+ )
164
+ Ap = ep * k**2 - An - As - Ae * ep / ee - Aw * ep / ew
165
+
166
+ elif method == "Ey":
167
+
168
+ # Ey
169
+
170
+ An = (
171
+ 8
172
+ * (q * (ep - es) + 2 * s * es)
173
+ * en
174
+ / (
175
+ (q * (ep - en) + 2 * n * en)
176
+ * (q**2 * (ep - es) + 4 * s**2 * es)
177
+ + (q * (ep - es) + 2 * s * es)
178
+ * (q**2 * (ep - en) + 4 * n**2 * en)
179
+ )
180
+ )
181
+ As = (
182
+ 8
183
+ * (q * (ep - en) + 2 * n * en)
184
+ * es
185
+ / (
186
+ (q * (ep - en) + 2 * n * en)
187
+ * (q**2 * (ep - es) + 4 * s**2 * es)
188
+ + (q * (ep - es) + 2 * s * es)
189
+ * (q**2 * (ep - en) + 4 * n**2 * en)
190
+ )
191
+ )
192
+ Ae = 2 / e / (e + w)
193
+ Aw = 2 / w / (e + w)
194
+ Ap = ep * k**2 - An * ep / en - As * ep / es - Ae - Aw
195
+
196
+ elif method == "scalar":
197
+
198
+ # scalar
199
+
200
+ An = 2 / n / (n + s)
201
+ As = 2 / s / (n + s)
202
+ Ae = 2 / e / (e + w)
203
+ Aw = 2 / w / (e + w)
204
+ Ap = ep * k**2 - An - As - Ae - Aw
205
+
206
+ else:
207
+
208
+ raise ValueError("unknown method")
209
+
210
+ ii = numpy.arange(nx * ny).reshape(nx, ny)
211
+
212
+ # north boundary
213
+ ib = ii[:, -1]
214
+ if boundary[0] == "S":
215
+ Ap[ib] += An[ib]
216
+ elif boundary[0] == "A":
217
+ Ap[ib] -= An[ib]
218
+ # else:
219
+ # raise ValueError('unknown boundary')
220
+
221
+ # south
222
+ ib = ii[:, 0]
223
+ if boundary[1] == "S":
224
+ Ap[ib] += As[ib]
225
+ elif boundary[1] == "A":
226
+ Ap[ib] -= As[ib]
227
+ # else:
228
+ # raise ValueError('unknown boundary')
229
+
230
+ # east
231
+ ib = ii[-1, :]
232
+ if boundary[2] == "S":
233
+ Ap[ib] += Ae[ib]
234
+ elif boundary[2] == "A":
235
+ Ap[ib] -= Ae[ib]
236
+ # else:
237
+ # raise ValueError('unknown boundary')
238
+
239
+ # west
240
+ ib = ii[0, :]
241
+ if boundary[3] == "S":
242
+ Ap[ib] += Aw[ib]
243
+ elif boundary[3] == "A":
244
+ Ap[ib] -= Aw[ib]
245
+ # else:
246
+ # raise ValueError('unknown boundary')
247
+
248
+ iall = ii.flatten()
249
+ i_n = ii[:, 1:].flatten()
250
+ i_s = ii[:, :-1].flatten()
251
+ i_e = ii[1:, :].flatten()
252
+ i_w = ii[:-1, :].flatten()
253
+
254
+ I = numpy.r_[iall, i_w, i_e, i_s, i_n]
255
+ J = numpy.r_[iall, i_e, i_w, i_n, i_s]
256
+ V = numpy.r_[Ap[iall], Ae[i_w], Aw[i_e], An[i_s], As[i_n]]
257
+
258
+ A = coo_matrix((V, (I, J))).tocsr()
259
+
260
+ return A
261
+
262
+ def solve(self, neigs, tol):
263
+
264
+ from scipy.sparse.linalg import eigen
265
+
266
+ self.nmodes = neigs
267
+ self.tol = tol
268
+
269
+ A = self.build_matrix()
270
+
271
+ eigvals, eigvecs = eigen.eigs( # type: ignore
272
+ A, k=neigs, which="LR", tol=tol, ncv=10 * neigs, return_eigenvectors=True
273
+ )
274
+
275
+ neff = self.wl * numpy.sqrt(eigvals) / (2 * numpy.pi)
276
+ phi = []
277
+ for ieig in range(neigs):
278
+ tmp = eigvecs[:, ieig].reshape(self.nx, self.ny)
279
+ phi.append(tmp)
280
+
281
+ # sort and save the modes
282
+ idx = numpy.flipud(numpy.argsort(neff))
283
+ self.neff = neff[idx]
284
+ tmp = []
285
+ for i in idx:
286
+ tmp.append(phi[i])
287
+
288
+ if self.method == "scalar":
289
+ self.phi = tmp
290
+ elif self.method == "Ex":
291
+ self.Ex = tmp
292
+ if self.method == "Ey":
293
+ self.Ey = tmp
294
+
295
+ return self
296
+
297
+ def __str__(self):
298
+ descr = (
299
+ "Semi-Vectorial Finite Difference Modesolver\n\tmethod: %s\n" % self.method
300
+ )
301
+ return descr
302
+
303
+
304
+ class VFDModeSolver(ModeSolver):
305
+
306
+ """
307
+ The VFDModeSolver class computes the electric and magnetic fields
308
+ for modes of a dielectric waveguide using the "Vector Finite
309
+ Difference (VFD)" method, as described in A. B. Fallahkhair,
310
+ K. S. Li and T. E. Murphy, "Vector Finite Difference Modesolver
311
+ for Anisotropic Dielectric Waveguides", J. Lightwave
312
+ Technol. 26(11), 1423-1431, (2008).
313
+
314
+
315
+ Parameters
316
+ ----------
317
+ wl : float
318
+ The wavelength of the optical radiation (units are arbitrary,
319
+ but must be self-consistent between all inputs. It is recommended to
320
+ just use microns for everthing)
321
+ x : 1D array of floats
322
+ Array of x-values
323
+ y : 1D array of floats
324
+ Array of y-values
325
+ epsfunc : function
326
+ This is a function that provides the relative permittivity
327
+ matrix (square of the refractive index) as a function of its x
328
+ and y numpy.arrays (the function's input parameters). The
329
+ function must be of the form: ``myRelativePermittivity(x,y)``
330
+ The function returns a relative permittivity numpy.array of either
331
+ shape( x.shape[0], y.shape[0] ) where each element of the
332
+ array can either be a single float, corresponding the an
333
+ isotropic refractive index, or (x.shape[0], y.shape[0], 5),
334
+ where the last dimension describes the relative permittivity in
335
+ the form (epsxx, epsxy, epsyx, epsyy, epszz).
336
+ boundary : str
337
+ This is a string that identifies the type of boundary
338
+ conditions applied.
339
+ The following options are available:
340
+ 'A' - Hx is antisymmetric, Hy is symmetric.
341
+ 'S' - Hx is symmetric and, Hy is antisymmetric.
342
+ '0' - Hx and Hy are zero immediately outside of the boundary.
343
+ The string identifies all four boundary conditions, in the
344
+ order: North, south, east, west. For example, boundary='000A'
345
+
346
+ Returns
347
+ -------
348
+ self : an instance of the VFDModeSolver class
349
+ Typically self.solve() will be called in order to actually
350
+ find the modes.
351
+
352
+ """
353
+
354
+ def __init__(self, wl, x, y, epsfunc, boundary):
355
+ self.wl = wl
356
+ self.x = x
357
+ self.y = y
358
+ self.epsfunc = epsfunc
359
+ self.boundary = boundary
360
+
361
+ def _get_eps(self, xc, yc):
362
+ tmp = self.epsfunc(xc, yc)
363
+
364
+ def _reshape(tmp):
365
+ """
366
+ pads the array by duplicating edge values
367
+ """
368
+ tmp = numpy.c_[tmp[:, 0:1], tmp, tmp[:, -1:]]
369
+ tmp = numpy.r_[tmp[0:1, :], tmp, tmp[-1:, :]]
370
+ return tmp
371
+
372
+ if tmp.ndim == 2: # isotropic refractive index
373
+ tmp = _reshape(tmp)
374
+ epsxx = epsyy = epszz = tmp
375
+ epsxy = epsyx = numpy.zeros_like(epsxx)
376
+
377
+ elif tmp.ndim == 3: # anisotropic refractive index
378
+ assert tmp.shape[2] == 5, "eps must be NxMx5"
379
+ epsxx = _reshape(tmp[:, :, 0])
380
+ epsxy = _reshape(tmp[:, :, 1])
381
+ epsyx = _reshape(tmp[:, :, 2])
382
+ epsyy = _reshape(tmp[:, :, 3])
383
+ epszz = _reshape(tmp[:, :, 4])
384
+
385
+ else:
386
+ raise ValueError("Invalid eps")
387
+
388
+ return epsxx, epsxy, epsyx, epsyy, epszz
389
+
390
+ def build_matrix(self):
391
+
392
+ from scipy.sparse import coo_matrix
393
+
394
+ wl = self.wl
395
+ x = self.x
396
+ y = self.y
397
+ boundary = self.boundary
398
+
399
+ dx = numpy.diff(x)
400
+ dy = numpy.diff(y)
401
+
402
+ dx = numpy.r_[dx[0], dx, dx[-1]].reshape(-1, 1)
403
+ dy = numpy.r_[dy[0], dy, dy[-1]].reshape(1, -1)
404
+
405
+ # Note: the permittivity is actually defined at the center of each
406
+ # region *between* the mesh points used for the H-field calculation.
407
+ # (See Fig. 1 of Fallahkhair and Murphy)
408
+ # In other words, eps is defined on (xc,yc) which is offset from
409
+ # (x,y), the grid where H is calculated, by
410
+ # "half a pixel" in the positive-x and positive-y directions.
411
+ xc = (x[:-1] + x[1:]) / 2
412
+ yc = (y[:-1] + y[1:]) / 2
413
+ epsxx, epsxy, epsyx, epsyy, epszz = self._get_eps(xc, yc)
414
+
415
+ nx = len(x)
416
+ ny = len(y)
417
+
418
+ self.nx = nx
419
+ self.ny = ny
420
+
421
+ k = 2 * numpy.pi / wl
422
+
423
+ ones_nx = numpy.ones((nx, 1))
424
+ ones_ny = numpy.ones((1, ny))
425
+
426
+ # distance of mesh points to nearest neighbor mesh point:
427
+ n = numpy.dot(ones_nx, dy[:, 1:]).flatten()
428
+ s = numpy.dot(ones_nx, dy[:, :-1]).flatten()
429
+ e = numpy.dot(dx[1:, :], ones_ny).flatten()
430
+ w = numpy.dot(dx[:-1, :], ones_ny).flatten()
431
+
432
+ # These define the permittivity (eps) tensor relative to each mesh point
433
+ # using the following geometry:
434
+ #
435
+ # NW------N------NE
436
+ # | | |
437
+ # | 1 n 4 |
438
+ # | | |
439
+ # W---w---P---e---E
440
+ # | | |
441
+ # | 2 s 3 |
442
+ # | | |
443
+ # SW------S------SE
444
+
445
+ exx1 = epsxx[:-1, 1:].flatten()
446
+ exx2 = epsxx[:-1, :-1].flatten()
447
+ exx3 = epsxx[1:, :-1].flatten()
448
+ exx4 = epsxx[1:, 1:].flatten()
449
+
450
+ eyy1 = epsyy[:-1, 1:].flatten()
451
+ eyy2 = epsyy[:-1, :-1].flatten()
452
+ eyy3 = epsyy[1:, :-1].flatten()
453
+ eyy4 = epsyy[1:, 1:].flatten()
454
+
455
+ exy1 = epsxy[:-1, 1:].flatten()
456
+ exy2 = epsxy[:-1, :-1].flatten()
457
+ exy3 = epsxy[1:, :-1].flatten()
458
+ exy4 = epsxy[1:, 1:].flatten()
459
+
460
+ eyx1 = epsyx[:-1, 1:].flatten()
461
+ eyx2 = epsyx[:-1, :-1].flatten()
462
+ eyx3 = epsyx[1:, :-1].flatten()
463
+ eyx4 = epsyx[1:, 1:].flatten()
464
+
465
+ ezz1 = epszz[:-1, 1:].flatten()
466
+ ezz2 = epszz[:-1, :-1].flatten()
467
+ ezz3 = epszz[1:, :-1].flatten()
468
+ ezz4 = epszz[1:, 1:].flatten()
469
+
470
+ ns21 = n * eyy2 + s * eyy1
471
+ ns34 = n * eyy3 + s * eyy4
472
+ ew14 = e * exx1 + w * exx4
473
+ ew23 = e * exx2 + w * exx3
474
+
475
+ # calculate the finite difference coefficients following
476
+ # Fallahkhair and Murphy, Appendix Eqs 21 though 37
477
+
478
+ axxn = (
479
+ (2 * eyy4 * e - eyx4 * n) * (eyy3 / ezz4) / ns34
480
+ + (2 * eyy1 * w + eyx1 * n) * (eyy2 / ezz1) / ns21
481
+ ) / (n * (e + w))
482
+ axxs = (
483
+ (2 * eyy3 * e + eyx3 * s) * (eyy4 / ezz3) / ns34
484
+ + (2 * eyy2 * w - eyx2 * s) * (eyy1 / ezz2) / ns21
485
+ ) / (s * (e + w))
486
+ ayye = (2 * n * exx4 - e * exy4) * exx1 / ezz4 / e / ew14 / (n + s) + (
487
+ 2 * s * exx3 + e * exy3
488
+ ) * exx2 / ezz3 / e / ew23 / (n + s)
489
+ ayyw = (2 * exx1 * n + exy1 * w) * exx4 / ezz1 / w / ew14 / (n + s) + (
490
+ 2 * exx2 * s - exy2 * w
491
+ ) * exx3 / ezz2 / w / ew23 / (n + s)
492
+ axxe = (
493
+ 2 / (e * (e + w))
494
+ + (eyy4 * eyx3 / ezz3 - eyy3 * eyx4 / ezz4) / (e + w) / ns34
495
+ )
496
+ axxw = (
497
+ 2 / (w * (e + w))
498
+ + (eyy2 * eyx1 / ezz1 - eyy1 * eyx2 / ezz2) / (e + w) / ns21
499
+ )
500
+ ayyn = (
501
+ 2 / (n * (n + s))
502
+ + (exx4 * exy1 / ezz1 - exx1 * exy4 / ezz4) / (n + s) / ew14
503
+ )
504
+ ayys = (
505
+ 2 / (s * (n + s))
506
+ + (exx2 * exy3 / ezz3 - exx3 * exy2 / ezz2) / (n + s) / ew23
507
+ )
508
+
509
+ axxne = +eyx4 * eyy3 / ezz4 / (e + w) / ns34
510
+ axxse = -eyx3 * eyy4 / ezz3 / (e + w) / ns34
511
+ axxnw = -eyx1 * eyy2 / ezz1 / (e + w) / ns21
512
+ axxsw = +eyx2 * eyy1 / ezz2 / (e + w) / ns21
513
+
514
+ ayyne = +exy4 * exx1 / ezz4 / (n + s) / ew14
515
+ ayyse = -exy3 * exx2 / ezz3 / (n + s) / ew23
516
+ ayynw = -exy1 * exx4 / ezz1 / (n + s) / ew14
517
+ ayysw = +exy2 * exx3 / ezz2 / (n + s) / ew23
518
+
519
+ axxp = (
520
+ -axxn
521
+ - axxs
522
+ - axxe
523
+ - axxw
524
+ - axxne
525
+ - axxse
526
+ - axxnw
527
+ - axxsw
528
+ + k**2
529
+ * (n + s)
530
+ * (eyy4 * eyy3 * e / ns34 + eyy1 * eyy2 * w / ns21)
531
+ / (e + w)
532
+ )
533
+ ayyp = (
534
+ -ayyn
535
+ - ayys
536
+ - ayye
537
+ - ayyw
538
+ - ayyne
539
+ - ayyse
540
+ - ayynw
541
+ - ayysw
542
+ + k**2
543
+ * (e + w)
544
+ * (exx1 * exx4 * n / ew14 + exx2 * exx3 * s / ew23)
545
+ / (n + s)
546
+ )
547
+ axyn = (
548
+ eyy3 * eyy4 / ezz4 / ns34
549
+ - eyy2 * eyy1 / ezz1 / ns21
550
+ + s * (eyy2 * eyy4 - eyy1 * eyy3) / ns21 / ns34
551
+ ) / (e + w)
552
+ axys = (
553
+ eyy1 * eyy2 / ezz2 / ns21
554
+ - eyy4 * eyy3 / ezz3 / ns34
555
+ + n * (eyy2 * eyy4 - eyy1 * eyy3) / ns21 / ns34
556
+ ) / (e + w)
557
+ ayxe = (
558
+ exx1 * exx4 / ezz4 / ew14
559
+ - exx2 * exx3 / ezz3 / ew23
560
+ + w * (exx2 * exx4 - exx1 * exx3) / ew23 / ew14
561
+ ) / (n + s)
562
+ ayxw = (
563
+ exx3 * exx2 / ezz2 / ew23
564
+ - exx4 * exx1 / ezz1 / ew14
565
+ + e * (exx4 * exx2 - exx1 * exx3) / ew23 / ew14
566
+ ) / (n + s)
567
+
568
+ axye = (eyy4 * (1 + eyy3 / ezz4) - eyy3 * (1 + eyy4 / ezz4)) / ns34 / (
569
+ e + w
570
+ ) - (
571
+ 2 * eyx1 * eyy2 / ezz1 * n * w / ns21
572
+ + 2 * eyx2 * eyy1 / ezz2 * s * w / ns21
573
+ + 2 * eyx4 * eyy3 / ezz4 * n * e / ns34
574
+ + 2 * eyx3 * eyy4 / ezz3 * s * e / ns34
575
+ + 2 * eyy1 * eyy2 * (1.0 / ezz1 - 1.0 / ezz2) * w**2 / ns21
576
+ ) / e / (
577
+ e + w
578
+ ) ** 2
579
+
580
+ axyw = (eyy2 * (1 + eyy1 / ezz2) - eyy1 * (1 + eyy2 / ezz2)) / ns21 / (
581
+ e + w
582
+ ) - (
583
+ 2 * eyx1 * eyy2 / ezz1 * n * e / ns21
584
+ + 2 * eyx2 * eyy1 / ezz2 * s * e / ns21
585
+ + 2 * eyx4 * eyy3 / ezz4 * n * w / ns34
586
+ + 2 * eyx3 * eyy4 / ezz3 * s * w / ns34
587
+ + 2 * eyy3 * eyy4 * (1.0 / ezz3 - 1.0 / ezz4) * e**2 / ns34
588
+ ) / w / (
589
+ e + w
590
+ ) ** 2
591
+
592
+ ayxn = (exx4 * (1 + exx1 / ezz4) - exx1 * (1 + exx4 / ezz4)) / ew14 / (
593
+ n + s
594
+ ) - (
595
+ 2 * exy3 * exx2 / ezz3 * e * s / ew23
596
+ + 2 * exy2 * exx3 / ezz2 * w * n / ew23
597
+ + 2 * exy4 * exx1 / ezz4 * e * s / ew14
598
+ + 2 * exy1 * exx4 / ezz1 * w * n / ew14
599
+ + 2 * exx3 * exx2 * (1.0 / ezz3 - 1.0 / ezz2) * s**2 / ew23
600
+ ) / n / (
601
+ n + s
602
+ ) ** 2
603
+
604
+ ayxs = (exx2 * (1 + exx3 / ezz2) - exx3 * (1 + exx2 / ezz2)) / ew23 / (
605
+ n + s
606
+ ) - (
607
+ 2 * exy3 * exx2 / ezz3 * e * n / ew23
608
+ + 2 * exy2 * exx3 / ezz2 * w * n / ew23
609
+ + 2 * exy4 * exx1 / ezz4 * e * s / ew14
610
+ + 2 * exy1 * exx4 / ezz1 * w * s / ew14
611
+ + 2 * exx1 * exx4 * (1.0 / ezz1 - 1.0 / ezz4) * n**2 / ew14
612
+ ) / s / (
613
+ n + s
614
+ ) ** 2
615
+
616
+ axyne = +eyy3 * (1 - eyy4 / ezz4) / (e + w) / ns34
617
+ axyse = -eyy4 * (1 - eyy3 / ezz3) / (e + w) / ns34
618
+ axynw = -eyy2 * (1 - eyy1 / ezz1) / (e + w) / ns21
619
+ axysw = +eyy1 * (1 - eyy2 / ezz2) / (e + w) / ns21
620
+
621
+ ayxne = +exx1 * (1 - exx4 / ezz4) / (n + s) / ew14
622
+ ayxse = -exx2 * (1 - exx3 / ezz3) / (n + s) / ew23
623
+ ayxnw = -exx4 * (1 - exx1 / ezz1) / (n + s) / ew14
624
+ ayxsw = +exx3 * (1 - exx2 / ezz2) / (n + s) / ew23
625
+
626
+ axyp = -(axyn + axys + axye + axyw + axyne + axyse + axynw + axysw) - k**2 * (
627
+ w * (n * eyx1 * eyy2 + s * eyx2 * eyy1) / ns21
628
+ + e * (s * eyx3 * eyy4 + n * eyx4 * eyy3) / ns34
629
+ ) / (e + w)
630
+ ayxp = -(ayxn + ayxs + ayxe + ayxw + ayxne + ayxse + ayxnw + ayxsw) - k**2 * (
631
+ n * (w * exy1 * exx4 + e * exy4 * exx1) / ew14
632
+ + s * (w * exy2 * exx3 + e * exy3 * exx2) / ew23
633
+ ) / (n + s)
634
+
635
+ ii = numpy.arange(nx * ny).reshape(nx, ny)
636
+
637
+ # NORTH boundary
638
+
639
+ ib = ii[:, -1]
640
+
641
+ if boundary[0] == "S":
642
+ sign = 1
643
+ elif boundary[0] == "A":
644
+ sign = -1
645
+ elif boundary[0] == "0":
646
+ sign = 0
647
+ else:
648
+ raise ValueError("unknown boundary conditions")
649
+
650
+ axxs[ib] += sign * axxn[ib]
651
+ axxse[ib] += sign * axxne[ib]
652
+ axxsw[ib] += sign * axxnw[ib]
653
+ ayxs[ib] += sign * ayxn[ib]
654
+ ayxse[ib] += sign * ayxne[ib]
655
+ ayxsw[ib] += sign * ayxnw[ib]
656
+ ayys[ib] -= sign * ayyn[ib]
657
+ ayyse[ib] -= sign * ayyne[ib]
658
+ ayysw[ib] -= sign * ayynw[ib]
659
+ axys[ib] -= sign * axyn[ib]
660
+ axyse[ib] -= sign * axyne[ib]
661
+ axysw[ib] -= sign * axynw[ib]
662
+
663
+ # SOUTH boundary
664
+
665
+ ib = ii[:, 0]
666
+
667
+ if boundary[1] == "S":
668
+ sign = 1
669
+ elif boundary[1] == "A":
670
+ sign = -1
671
+ elif boundary[1] == "0":
672
+ sign = 0
673
+ else:
674
+ raise ValueError("unknown boundary conditions")
675
+
676
+ axxn[ib] += sign * axxs[ib]
677
+ axxne[ib] += sign * axxse[ib]
678
+ axxnw[ib] += sign * axxsw[ib]
679
+ ayxn[ib] += sign * ayxs[ib]
680
+ ayxne[ib] += sign * ayxse[ib]
681
+ ayxnw[ib] += sign * ayxsw[ib]
682
+ ayyn[ib] -= sign * ayys[ib]
683
+ ayyne[ib] -= sign * ayyse[ib]
684
+ ayynw[ib] -= sign * ayysw[ib]
685
+ axyn[ib] -= sign * axys[ib]
686
+ axyne[ib] -= sign * axyse[ib]
687
+ axynw[ib] -= sign * axysw[ib]
688
+
689
+ # EAST boundary
690
+
691
+ ib = ii[-1, :]
692
+
693
+ if boundary[2] == "S":
694
+ sign = 1
695
+ elif boundary[2] == "A":
696
+ sign = -1
697
+ elif boundary[2] == "0":
698
+ sign = 0
699
+ else:
700
+ raise ValueError("unknown boundary conditions")
701
+
702
+ axxw[ib] += sign * axxe[ib]
703
+ axxnw[ib] += sign * axxne[ib]
704
+ axxsw[ib] += sign * axxse[ib]
705
+ ayxw[ib] += sign * ayxe[ib]
706
+ ayxnw[ib] += sign * ayxne[ib]
707
+ ayxsw[ib] += sign * ayxse[ib]
708
+ ayyw[ib] -= sign * ayye[ib]
709
+ ayynw[ib] -= sign * ayyne[ib]
710
+ ayysw[ib] -= sign * ayyse[ib]
711
+ axyw[ib] -= sign * axye[ib]
712
+ axynw[ib] -= sign * axyne[ib]
713
+ axysw[ib] -= sign * axyse[ib]
714
+
715
+ # WEST boundary
716
+
717
+ ib = ii[0, :]
718
+
719
+ if boundary[3] == "S":
720
+ sign = 1
721
+ elif boundary[3] == "A":
722
+ sign = -1
723
+ elif boundary[3] == "0":
724
+ sign = 0
725
+ else:
726
+ raise ValueError("unknown boundary conditions")
727
+
728
+ axxe[ib] += sign * axxw[ib]
729
+ axxne[ib] += sign * axxnw[ib]
730
+ axxse[ib] += sign * axxsw[ib]
731
+ ayxe[ib] += sign * ayxw[ib]
732
+ ayxne[ib] += sign * ayxnw[ib]
733
+ ayxse[ib] += sign * ayxsw[ib]
734
+ ayye[ib] -= sign * ayyw[ib]
735
+ ayyne[ib] -= sign * ayynw[ib]
736
+ ayyse[ib] -= sign * ayysw[ib]
737
+ axye[ib] -= sign * axyw[ib]
738
+ axyne[ib] -= sign * axynw[ib]
739
+ axyse[ib] -= sign * axysw[ib]
740
+
741
+ # Assemble sparse matrix
742
+
743
+ iall = ii.flatten()
744
+ i_s = ii[:, :-1].flatten()
745
+ i_n = ii[:, 1:].flatten()
746
+ i_e = ii[1:, :].flatten()
747
+ i_w = ii[:-1, :].flatten()
748
+ i_ne = ii[1:, 1:].flatten()
749
+ i_se = ii[1:, :-1].flatten()
750
+ i_sw = ii[:-1, :-1].flatten()
751
+ i_nw = ii[:-1, 1:].flatten()
752
+
753
+ Ixx = numpy.r_[iall, i_w, i_e, i_s, i_n, i_ne, i_se, i_sw, i_nw]
754
+ Jxx = numpy.r_[iall, i_e, i_w, i_n, i_s, i_sw, i_nw, i_ne, i_se]
755
+ Vxx = numpy.r_[
756
+ axxp[iall],
757
+ axxe[i_w],
758
+ axxw[i_e],
759
+ axxn[i_s],
760
+ axxs[i_n],
761
+ axxsw[i_ne],
762
+ axxnw[i_se],
763
+ axxne[i_sw],
764
+ axxse[i_nw],
765
+ ]
766
+
767
+ Ixy = numpy.r_[iall, i_w, i_e, i_s, i_n, i_ne, i_se, i_sw, i_nw]
768
+ Jxy = numpy.r_[iall, i_e, i_w, i_n, i_s, i_sw, i_nw, i_ne, i_se] + nx * ny
769
+ Vxy = numpy.r_[
770
+ axyp[iall],
771
+ axye[i_w],
772
+ axyw[i_e],
773
+ axyn[i_s],
774
+ axys[i_n],
775
+ axysw[i_ne],
776
+ axynw[i_se],
777
+ axyne[i_sw],
778
+ axyse[i_nw],
779
+ ]
780
+
781
+ Iyx = numpy.r_[iall, i_w, i_e, i_s, i_n, i_ne, i_se, i_sw, i_nw] + nx * ny
782
+ Jyx = numpy.r_[iall, i_e, i_w, i_n, i_s, i_sw, i_nw, i_ne, i_se]
783
+ Vyx = numpy.r_[
784
+ ayxp[iall],
785
+ ayxe[i_w],
786
+ ayxw[i_e],
787
+ ayxn[i_s],
788
+ ayxs[i_n],
789
+ ayxsw[i_ne],
790
+ ayxnw[i_se],
791
+ ayxne[i_sw],
792
+ ayxse[i_nw],
793
+ ]
794
+
795
+ Iyy = numpy.r_[iall, i_w, i_e, i_s, i_n, i_ne, i_se, i_sw, i_nw] + nx * ny
796
+ Jyy = numpy.r_[iall, i_e, i_w, i_n, i_s, i_sw, i_nw, i_ne, i_se] + nx * ny
797
+ Vyy = numpy.r_[
798
+ ayyp[iall],
799
+ ayye[i_w],
800
+ ayyw[i_e],
801
+ ayyn[i_s],
802
+ ayys[i_n],
803
+ ayysw[i_ne],
804
+ ayynw[i_se],
805
+ ayyne[i_sw],
806
+ ayyse[i_nw],
807
+ ]
808
+
809
+ I = numpy.r_[Ixx, Ixy, Iyx, Iyy]
810
+ J = numpy.r_[Jxx, Jxy, Jyx, Jyy]
811
+ V = numpy.r_[Vxx, Vxy, Vyx, Vyy]
812
+ A = coo_matrix((V, (I, J))).tocsr()
813
+
814
+ return A
815
+
816
+ def compute_other_fields(self, neffs, Hxs, Hys):
817
+
818
+ from scipy.sparse import coo_matrix
819
+
820
+ wl = self.wl
821
+ x = self.x
822
+ y = self.y
823
+ boundary = self.boundary
824
+
825
+ Hzs = []
826
+ Exs = []
827
+ Eys = []
828
+ Ezs = []
829
+ for neff, Hx, Hy in zip(neffs, Hxs, Hys):
830
+
831
+ dx = numpy.diff(x)
832
+ dy = numpy.diff(y)
833
+
834
+ dx = numpy.r_[dx[0], dx, dx[-1]].reshape(-1, 1)
835
+ dy = numpy.r_[dy[0], dy, dy[-1]].reshape(1, -1)
836
+
837
+ xc = (x[:-1] + x[1:]) / 2
838
+ yc = (y[:-1] + y[1:]) / 2
839
+ epsxx, epsxy, epsyx, epsyy, epszz = self._get_eps(xc, yc)
840
+
841
+ nx = len(x)
842
+ ny = len(y)
843
+
844
+ k = 2 * numpy.pi / wl
845
+
846
+ ones_nx = numpy.ones((nx, 1))
847
+ ones_ny = numpy.ones((1, ny))
848
+
849
+ n = numpy.dot(ones_nx, dy[:, 1:]).flatten()
850
+ s = numpy.dot(ones_nx, dy[:, :-1]).flatten()
851
+ e = numpy.dot(dx[1:, :], ones_ny).flatten()
852
+ w = numpy.dot(dx[:-1, :], ones_ny).flatten()
853
+
854
+ exx1 = epsxx[:-1, 1:].flatten()
855
+ exx2 = epsxx[:-1, :-1].flatten()
856
+ exx3 = epsxx[1:, :-1].flatten()
857
+ exx4 = epsxx[1:, 1:].flatten()
858
+
859
+ eyy1 = epsyy[:-1, 1:].flatten()
860
+ eyy2 = epsyy[:-1, :-1].flatten()
861
+ eyy3 = epsyy[1:, :-1].flatten()
862
+ eyy4 = epsyy[1:, 1:].flatten()
863
+
864
+ exy1 = epsxy[:-1, 1:].flatten()
865
+ exy2 = epsxy[:-1, :-1].flatten()
866
+ exy3 = epsxy[1:, :-1].flatten()
867
+ exy4 = epsxy[1:, 1:].flatten()
868
+
869
+ eyx1 = epsyx[:-1, 1:].flatten()
870
+ eyx2 = epsyx[:-1, :-1].flatten()
871
+ eyx3 = epsyx[1:, :-1].flatten()
872
+ eyx4 = epsyx[1:, 1:].flatten()
873
+
874
+ ezz1 = epszz[:-1, 1:].flatten()
875
+ ezz2 = epszz[:-1, :-1].flatten()
876
+ ezz3 = epszz[1:, :-1].flatten()
877
+ ezz4 = epszz[1:, 1:].flatten()
878
+
879
+ b = neff * k
880
+
881
+ bzxne = (
882
+ 0.5
883
+ * (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2)
884
+ * eyx4
885
+ / ezz4
886
+ / (n * eyy3 + s * eyy4)
887
+ / ezz2
888
+ / ezz1
889
+ / (n * eyy2 + s * eyy1)
890
+ / (e + w)
891
+ * eyy3
892
+ * eyy1
893
+ * w
894
+ * eyy2
895
+ + 0.5
896
+ * (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e)
897
+ * (1 - exx4 / ezz4)
898
+ / ezz3
899
+ / ezz2
900
+ / (w * exx3 + e * exx2)
901
+ / (w * exx4 + e * exx1)
902
+ / (n + s)
903
+ * exx2
904
+ * exx3
905
+ * exx1
906
+ * s
907
+ ) / b
908
+
909
+ bzxse = (
910
+ -0.5
911
+ * (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2)
912
+ * eyx3
913
+ / ezz3
914
+ / (n * eyy3 + s * eyy4)
915
+ / ezz2
916
+ / ezz1
917
+ / (n * eyy2 + s * eyy1)
918
+ / (e + w)
919
+ * eyy4
920
+ * eyy1
921
+ * w
922
+ * eyy2
923
+ + 0.5
924
+ * (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e)
925
+ * (1 - exx3 / ezz3)
926
+ / (w * exx3 + e * exx2)
927
+ / ezz4
928
+ / ezz1
929
+ / (w * exx4 + e * exx1)
930
+ / (n + s)
931
+ * exx2
932
+ * n
933
+ * exx1
934
+ * exx4
935
+ ) / b
936
+
937
+ bzxnw = (
938
+ -0.5
939
+ * (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3)
940
+ * eyx1
941
+ / ezz4
942
+ / ezz3
943
+ / (n * eyy3 + s * eyy4)
944
+ / ezz1
945
+ / (n * eyy2 + s * eyy1)
946
+ / (e + w)
947
+ * eyy4
948
+ * eyy3
949
+ * eyy2
950
+ * e
951
+ - 0.5
952
+ * (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e)
953
+ * (1 - exx1 / ezz1)
954
+ / ezz3
955
+ / ezz2
956
+ / (w * exx3 + e * exx2)
957
+ / (w * exx4 + e * exx1)
958
+ / (n + s)
959
+ * exx2
960
+ * exx3
961
+ * exx4
962
+ * s
963
+ ) / b
964
+
965
+ bzxsw = (
966
+ 0.5
967
+ * (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3)
968
+ * eyx2
969
+ / ezz4
970
+ / ezz3
971
+ / (n * eyy3 + s * eyy4)
972
+ / ezz2
973
+ / (n * eyy2 + s * eyy1)
974
+ / (e + w)
975
+ * eyy4
976
+ * eyy3
977
+ * eyy1
978
+ * e
979
+ - 0.5
980
+ * (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e)
981
+ * (1 - exx2 / ezz2)
982
+ / (w * exx3 + e * exx2)
983
+ / ezz4
984
+ / ezz1
985
+ / (w * exx4 + e * exx1)
986
+ / (n + s)
987
+ * exx3
988
+ * n
989
+ * exx1
990
+ * exx4
991
+ ) / b
992
+
993
+ bzxn = (
994
+ (
995
+ 0.5
996
+ * (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3)
997
+ * n
998
+ * ezz1
999
+ * ezz2
1000
+ / eyy1
1001
+ * (2 * eyy1 / ezz1 / n**2 + eyx1 / ezz1 / n / w)
1002
+ + 0.5
1003
+ * (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2)
1004
+ * n
1005
+ * ezz4
1006
+ * ezz3
1007
+ / eyy4
1008
+ * (2 * eyy4 / ezz4 / n**2 - eyx4 / ezz4 / n / e)
1009
+ )
1010
+ / ezz4
1011
+ / ezz3
1012
+ / (n * eyy3 + s * eyy4)
1013
+ / ezz2
1014
+ / ezz1
1015
+ / (n * eyy2 + s * eyy1)
1016
+ / (e + w)
1017
+ * eyy4
1018
+ * eyy3
1019
+ * eyy1
1020
+ * w
1021
+ * eyy2
1022
+ * e
1023
+ + (
1024
+ (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e)
1025
+ * (
1026
+ 0.5
1027
+ * ezz4
1028
+ * (
1029
+ (1 - exx1 / ezz1) / n / w
1030
+ - exy1 / ezz1 * (2.0 / n**2 - 2 / n**2 * s / (n + s))
1031
+ )
1032
+ / exx1
1033
+ * ezz1
1034
+ * w
1035
+ + (ezz4 - ezz1) * s / n / (n + s)
1036
+ + 0.5
1037
+ * ezz1
1038
+ * (
1039
+ -(1 - exx4 / ezz4) / n / e
1040
+ - exy4 / ezz4 * (2.0 / n**2 - 2 / n**2 * s / (n + s))
1041
+ )
1042
+ / exx4
1043
+ * ezz4
1044
+ * e
1045
+ )
1046
+ - (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e)
1047
+ * (
1048
+ -ezz3 * exy2 / n / (n + s) / exx2 * w
1049
+ + (ezz3 - ezz2) * s / n / (n + s)
1050
+ - ezz2 * exy3 / n / (n + s) / exx3 * e
1051
+ )
1052
+ )
1053
+ / ezz3
1054
+ / ezz2
1055
+ / (w * exx3 + e * exx2)
1056
+ / ezz4
1057
+ / ezz1
1058
+ / (w * exx4 + e * exx1)
1059
+ / (n + s)
1060
+ * exx2
1061
+ * exx3
1062
+ * n
1063
+ * exx1
1064
+ * exx4
1065
+ * s
1066
+ ) / b
1067
+
1068
+ bzxs = (
1069
+ (
1070
+ 0.5
1071
+ * (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3)
1072
+ * s
1073
+ * ezz2
1074
+ * ezz1
1075
+ / eyy2
1076
+ * (2 * eyy2 / ezz2 / s**2 - eyx2 / ezz2 / s / w)
1077
+ + 0.5
1078
+ * (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2)
1079
+ * s
1080
+ * ezz3
1081
+ * ezz4
1082
+ / eyy3
1083
+ * (2 * eyy3 / ezz3 / s**2 + eyx3 / ezz3 / s / e)
1084
+ )
1085
+ / ezz4
1086
+ / ezz3
1087
+ / (n * eyy3 + s * eyy4)
1088
+ / ezz2
1089
+ / ezz1
1090
+ / (n * eyy2 + s * eyy1)
1091
+ / (e + w)
1092
+ * eyy4
1093
+ * eyy3
1094
+ * eyy1
1095
+ * w
1096
+ * eyy2
1097
+ * e
1098
+ + (
1099
+ (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e)
1100
+ * (
1101
+ -ezz4 * exy1 / s / (n + s) / exx1 * w
1102
+ - (ezz4 - ezz1) * n / s / (n + s)
1103
+ - ezz1 * exy4 / s / (n + s) / exx4 * e
1104
+ )
1105
+ - (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e)
1106
+ * (
1107
+ 0.5
1108
+ * ezz3
1109
+ * (
1110
+ -(1 - exx2 / ezz2) / s / w
1111
+ - exy2 / ezz2 * (2.0 / s**2 - 2 / s**2 * n / (n + s))
1112
+ )
1113
+ / exx2
1114
+ * ezz2
1115
+ * w
1116
+ - (ezz3 - ezz2) * n / s / (n + s)
1117
+ + 0.5
1118
+ * ezz2
1119
+ * (
1120
+ (1 - exx3 / ezz3) / s / e
1121
+ - exy3 / ezz3 * (2.0 / s**2 - 2 / s**2 * n / (n + s))
1122
+ )
1123
+ / exx3
1124
+ * ezz3
1125
+ * e
1126
+ )
1127
+ )
1128
+ / ezz3
1129
+ / ezz2
1130
+ / (w * exx3 + e * exx2)
1131
+ / ezz4
1132
+ / ezz1
1133
+ / (w * exx4 + e * exx1)
1134
+ / (n + s)
1135
+ * exx2
1136
+ * exx3
1137
+ * n
1138
+ * exx1
1139
+ * exx4
1140
+ * s
1141
+ ) / b
1142
+
1143
+ bzxe = (
1144
+ (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2)
1145
+ * (
1146
+ 0.5 * n * ezz4 * ezz3 / eyy4 * (2.0 / e**2 - eyx4 / ezz4 / n / e)
1147
+ + 0.5
1148
+ * s
1149
+ * ezz3
1150
+ * ezz4
1151
+ / eyy3
1152
+ * (2.0 / e**2 + eyx3 / ezz3 / s / e)
1153
+ )
1154
+ / ezz4
1155
+ / ezz3
1156
+ / (n * eyy3 + s * eyy4)
1157
+ / ezz2
1158
+ / ezz1
1159
+ / (n * eyy2 + s * eyy1)
1160
+ / (e + w)
1161
+ * eyy4
1162
+ * eyy3
1163
+ * eyy1
1164
+ * w
1165
+ * eyy2
1166
+ * e
1167
+ + (
1168
+ -0.5
1169
+ * (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e)
1170
+ * ezz1
1171
+ * (1 - exx4 / ezz4)
1172
+ / n
1173
+ / exx4
1174
+ * ezz4
1175
+ - 0.5
1176
+ * (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e)
1177
+ * ezz2
1178
+ * (1 - exx3 / ezz3)
1179
+ / s
1180
+ / exx3
1181
+ * ezz3
1182
+ )
1183
+ / ezz3
1184
+ / ezz2
1185
+ / (w * exx3 + e * exx2)
1186
+ / ezz4
1187
+ / ezz1
1188
+ / (w * exx4 + e * exx1)
1189
+ / (n + s)
1190
+ * exx2
1191
+ * exx3
1192
+ * n
1193
+ * exx1
1194
+ * exx4
1195
+ * s
1196
+ ) / b
1197
+
1198
+ bzxw = (
1199
+ (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3)
1200
+ * (
1201
+ 0.5 * n * ezz1 * ezz2 / eyy1 * (2.0 / w**2 + eyx1 / ezz1 / n / w)
1202
+ + 0.5
1203
+ * s
1204
+ * ezz2
1205
+ * ezz1
1206
+ / eyy2
1207
+ * (2.0 / w**2 - eyx2 / ezz2 / s / w)
1208
+ )
1209
+ / ezz4
1210
+ / ezz3
1211
+ / (n * eyy3 + s * eyy4)
1212
+ / ezz2
1213
+ / ezz1
1214
+ / (n * eyy2 + s * eyy1)
1215
+ / (e + w)
1216
+ * eyy4
1217
+ * eyy3
1218
+ * eyy1
1219
+ * w
1220
+ * eyy2
1221
+ * e
1222
+ + (
1223
+ 0.5
1224
+ * (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e)
1225
+ * ezz4
1226
+ * (1 - exx1 / ezz1)
1227
+ / n
1228
+ / exx1
1229
+ * ezz1
1230
+ + 0.5
1231
+ * (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e)
1232
+ * ezz3
1233
+ * (1 - exx2 / ezz2)
1234
+ / s
1235
+ / exx2
1236
+ * ezz2
1237
+ )
1238
+ / ezz3
1239
+ / ezz2
1240
+ / (w * exx3 + e * exx2)
1241
+ / ezz4
1242
+ / ezz1
1243
+ / (w * exx4 + e * exx1)
1244
+ / (n + s)
1245
+ * exx2
1246
+ * exx3
1247
+ * n
1248
+ * exx1
1249
+ * exx4
1250
+ * s
1251
+ ) / b
1252
+
1253
+ bzxp = (
1254
+ (
1255
+ (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3)
1256
+ * (
1257
+ 0.5
1258
+ * n
1259
+ * ezz1
1260
+ * ezz2
1261
+ / eyy1
1262
+ * (
1263
+ -2.0 / w**2
1264
+ - 2 * eyy1 / ezz1 / n**2
1265
+ + k**2 * eyy1
1266
+ - eyx1 / ezz1 / n / w
1267
+ )
1268
+ + 0.5
1269
+ * s
1270
+ * ezz2
1271
+ * ezz1
1272
+ / eyy2
1273
+ * (
1274
+ -2.0 / w**2
1275
+ - 2 * eyy2 / ezz2 / s**2
1276
+ + k**2 * eyy2
1277
+ + eyx2 / ezz2 / s / w
1278
+ )
1279
+ )
1280
+ + (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2)
1281
+ * (
1282
+ 0.5
1283
+ * n
1284
+ * ezz4
1285
+ * ezz3
1286
+ / eyy4
1287
+ * (
1288
+ -2.0 / e**2
1289
+ - 2 * eyy4 / ezz4 / n**2
1290
+ + k**2 * eyy4
1291
+ + eyx4 / ezz4 / n / e
1292
+ )
1293
+ + 0.5
1294
+ * s
1295
+ * ezz3
1296
+ * ezz4
1297
+ / eyy3
1298
+ * (
1299
+ -2.0 / e**2
1300
+ - 2 * eyy3 / ezz3 / s**2
1301
+ + k**2 * eyy3
1302
+ - eyx3 / ezz3 / s / e
1303
+ )
1304
+ )
1305
+ )
1306
+ / ezz4
1307
+ / ezz3
1308
+ / (n * eyy3 + s * eyy4)
1309
+ / ezz2
1310
+ / ezz1
1311
+ / (n * eyy2 + s * eyy1)
1312
+ / (e + w)
1313
+ * eyy4
1314
+ * eyy3
1315
+ * eyy1
1316
+ * w
1317
+ * eyy2
1318
+ * e
1319
+ + (
1320
+ (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e)
1321
+ * (
1322
+ 0.5
1323
+ * ezz4
1324
+ * (
1325
+ -(k**2) * exy1
1326
+ - (1 - exx1 / ezz1) / n / w
1327
+ - exy1 / ezz1 * (-2.0 / n**2 - 2 / n**2 * (n - s) / s)
1328
+ )
1329
+ / exx1
1330
+ * ezz1
1331
+ * w
1332
+ + (ezz4 - ezz1) * (n - s) / n / s
1333
+ + 0.5
1334
+ * ezz1
1335
+ * (
1336
+ -(k**2) * exy4
1337
+ + (1 - exx4 / ezz4) / n / e
1338
+ - exy4 / ezz4 * (-2.0 / n**2 - 2 / n**2 * (n - s) / s)
1339
+ )
1340
+ / exx4
1341
+ * ezz4
1342
+ * e
1343
+ )
1344
+ - (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e)
1345
+ * (
1346
+ 0.5
1347
+ * ezz3
1348
+ * (
1349
+ -(k**2) * exy2
1350
+ + (1 - exx2 / ezz2) / s / w
1351
+ - exy2 / ezz2 * (-2.0 / s**2 + 2 / s**2 * (n - s) / n)
1352
+ )
1353
+ / exx2
1354
+ * ezz2
1355
+ * w
1356
+ + (ezz3 - ezz2) * (n - s) / n / s
1357
+ + 0.5
1358
+ * ezz2
1359
+ * (
1360
+ -(k**2) * exy3
1361
+ - (1 - exx3 / ezz3) / s / e
1362
+ - exy3 / ezz3 * (-2.0 / s**2 + 2 / s**2 * (n - s) / n)
1363
+ )
1364
+ / exx3
1365
+ * ezz3
1366
+ * e
1367
+ )
1368
+ )
1369
+ / ezz3
1370
+ / ezz2
1371
+ / (w * exx3 + e * exx2)
1372
+ / ezz4
1373
+ / ezz1
1374
+ / (w * exx4 + e * exx1)
1375
+ / (n + s)
1376
+ * exx2
1377
+ * exx3
1378
+ * n
1379
+ * exx1
1380
+ * exx4
1381
+ * s
1382
+ ) / b
1383
+
1384
+ bzyne = (
1385
+ 0.5
1386
+ * (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2)
1387
+ * (1 - eyy4 / ezz4)
1388
+ / (n * eyy3 + s * eyy4)
1389
+ / ezz2
1390
+ / ezz1
1391
+ / (n * eyy2 + s * eyy1)
1392
+ / (e + w)
1393
+ * eyy3
1394
+ * eyy1
1395
+ * w
1396
+ * eyy2
1397
+ + 0.5
1398
+ * (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e)
1399
+ * exy4
1400
+ / ezz3
1401
+ / ezz2
1402
+ / (w * exx3 + e * exx2)
1403
+ / ezz4
1404
+ / (w * exx4 + e * exx1)
1405
+ / (n + s)
1406
+ * exx2
1407
+ * exx3
1408
+ * exx1
1409
+ * s
1410
+ ) / b
1411
+
1412
+ bzyse = (
1413
+ -0.5
1414
+ * (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2)
1415
+ * (1 - eyy3 / ezz3)
1416
+ / (n * eyy3 + s * eyy4)
1417
+ / ezz2
1418
+ / ezz1
1419
+ / (n * eyy2 + s * eyy1)
1420
+ / (e + w)
1421
+ * eyy4
1422
+ * eyy1
1423
+ * w
1424
+ * eyy2
1425
+ + 0.5
1426
+ * (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e)
1427
+ * exy3
1428
+ / ezz3
1429
+ / (w * exx3 + e * exx2)
1430
+ / ezz4
1431
+ / ezz1
1432
+ / (w * exx4 + e * exx1)
1433
+ / (n + s)
1434
+ * exx2
1435
+ * n
1436
+ * exx1
1437
+ * exx4
1438
+ ) / b
1439
+
1440
+ bzynw = (
1441
+ -0.5
1442
+ * (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3)
1443
+ * (1 - eyy1 / ezz1)
1444
+ / ezz4
1445
+ / ezz3
1446
+ / (n * eyy3 + s * eyy4)
1447
+ / (n * eyy2 + s * eyy1)
1448
+ / (e + w)
1449
+ * eyy4
1450
+ * eyy3
1451
+ * eyy2
1452
+ * e
1453
+ - 0.5
1454
+ * (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e)
1455
+ * exy1
1456
+ / ezz3
1457
+ / ezz2
1458
+ / (w * exx3 + e * exx2)
1459
+ / ezz1
1460
+ / (w * exx4 + e * exx1)
1461
+ / (n + s)
1462
+ * exx2
1463
+ * exx3
1464
+ * exx4
1465
+ * s
1466
+ ) / b
1467
+
1468
+ bzysw = (
1469
+ 0.5
1470
+ * (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3)
1471
+ * (1 - eyy2 / ezz2)
1472
+ / ezz4
1473
+ / ezz3
1474
+ / (n * eyy3 + s * eyy4)
1475
+ / (n * eyy2 + s * eyy1)
1476
+ / (e + w)
1477
+ * eyy4
1478
+ * eyy3
1479
+ * eyy1
1480
+ * e
1481
+ - 0.5
1482
+ * (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e)
1483
+ * exy2
1484
+ / ezz2
1485
+ / (w * exx3 + e * exx2)
1486
+ / ezz4
1487
+ / ezz1
1488
+ / (w * exx4 + e * exx1)
1489
+ / (n + s)
1490
+ * exx3
1491
+ * n
1492
+ * exx1
1493
+ * exx4
1494
+ ) / b
1495
+
1496
+ bzyn = (
1497
+ (
1498
+ 0.5
1499
+ * (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3)
1500
+ * ezz1
1501
+ * ezz2
1502
+ / eyy1
1503
+ * (1 - eyy1 / ezz1)
1504
+ / w
1505
+ - 0.5
1506
+ * (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2)
1507
+ * ezz4
1508
+ * ezz3
1509
+ / eyy4
1510
+ * (1 - eyy4 / ezz4)
1511
+ / e
1512
+ )
1513
+ / ezz4
1514
+ / ezz3
1515
+ / (n * eyy3 + s * eyy4)
1516
+ / ezz2
1517
+ / ezz1
1518
+ / (n * eyy2 + s * eyy1)
1519
+ / (e + w)
1520
+ * eyy4
1521
+ * eyy3
1522
+ * eyy1
1523
+ * w
1524
+ * eyy2
1525
+ * e
1526
+ + (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e)
1527
+ * (
1528
+ 0.5 * ezz4 * (2.0 / n**2 + exy1 / ezz1 / n / w) / exx1 * ezz1 * w
1529
+ + 0.5
1530
+ * ezz1
1531
+ * (2.0 / n**2 - exy4 / ezz4 / n / e)
1532
+ / exx4
1533
+ * ezz4
1534
+ * e
1535
+ )
1536
+ / ezz3
1537
+ / ezz2
1538
+ / (w * exx3 + e * exx2)
1539
+ / ezz4
1540
+ / ezz1
1541
+ / (w * exx4 + e * exx1)
1542
+ / (n + s)
1543
+ * exx2
1544
+ * exx3
1545
+ * n
1546
+ * exx1
1547
+ * exx4
1548
+ * s
1549
+ ) / b
1550
+
1551
+ bzys = (
1552
+ (
1553
+ -0.5
1554
+ * (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3)
1555
+ * ezz2
1556
+ * ezz1
1557
+ / eyy2
1558
+ * (1 - eyy2 / ezz2)
1559
+ / w
1560
+ + 0.5
1561
+ * (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2)
1562
+ * ezz3
1563
+ * ezz4
1564
+ / eyy3
1565
+ * (1 - eyy3 / ezz3)
1566
+ / e
1567
+ )
1568
+ / ezz4
1569
+ / ezz3
1570
+ / (n * eyy3 + s * eyy4)
1571
+ / ezz2
1572
+ / ezz1
1573
+ / (n * eyy2 + s * eyy1)
1574
+ / (e + w)
1575
+ * eyy4
1576
+ * eyy3
1577
+ * eyy1
1578
+ * w
1579
+ * eyy2
1580
+ * e
1581
+ - (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e)
1582
+ * (
1583
+ 0.5 * ezz3 * (2.0 / s**2 - exy2 / ezz2 / s / w) / exx2 * ezz2 * w
1584
+ + 0.5
1585
+ * ezz2
1586
+ * (2.0 / s**2 + exy3 / ezz3 / s / e)
1587
+ / exx3
1588
+ * ezz3
1589
+ * e
1590
+ )
1591
+ / ezz3
1592
+ / ezz2
1593
+ / (w * exx3 + e * exx2)
1594
+ / ezz4
1595
+ / ezz1
1596
+ / (w * exx4 + e * exx1)
1597
+ / (n + s)
1598
+ * exx2
1599
+ * exx3
1600
+ * n
1601
+ * exx1
1602
+ * exx4
1603
+ * s
1604
+ ) / b
1605
+
1606
+ bzye = (
1607
+ (
1608
+ (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3)
1609
+ * (
1610
+ -n * ezz2 / eyy1 * eyx1 / e / (e + w)
1611
+ + (ezz1 - ezz2) * w / e / (e + w)
1612
+ - s * ezz1 / eyy2 * eyx2 / e / (e + w)
1613
+ )
1614
+ + (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2)
1615
+ * (
1616
+ 0.5
1617
+ * n
1618
+ * ezz4
1619
+ * ezz3
1620
+ / eyy4
1621
+ * (
1622
+ -(1 - eyy4 / ezz4) / n / e
1623
+ - eyx4 / ezz4 * (2.0 / e**2 - 2 / e**2 * w / (e + w))
1624
+ )
1625
+ + 0.5
1626
+ * s
1627
+ * ezz3
1628
+ * ezz4
1629
+ / eyy3
1630
+ * (
1631
+ (1 - eyy3 / ezz3) / s / e
1632
+ - eyx3 / ezz3 * (2.0 / e**2 - 2 / e**2 * w / (e + w))
1633
+ )
1634
+ + (ezz4 - ezz3) * w / e / (e + w)
1635
+ )
1636
+ )
1637
+ / ezz4
1638
+ / ezz3
1639
+ / (n * eyy3 + s * eyy4)
1640
+ / ezz2
1641
+ / ezz1
1642
+ / (n * eyy2 + s * eyy1)
1643
+ / (e + w)
1644
+ * eyy4
1645
+ * eyy3
1646
+ * eyy1
1647
+ * w
1648
+ * eyy2
1649
+ * e
1650
+ + (
1651
+ 0.5
1652
+ * (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e)
1653
+ * ezz1
1654
+ * (2 * exx4 / ezz4 / e**2 - exy4 / ezz4 / n / e)
1655
+ / exx4
1656
+ * ezz4
1657
+ * e
1658
+ - 0.5
1659
+ * (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e)
1660
+ * ezz2
1661
+ * (2 * exx3 / ezz3 / e**2 + exy3 / ezz3 / s / e)
1662
+ / exx3
1663
+ * ezz3
1664
+ * e
1665
+ )
1666
+ / ezz3
1667
+ / ezz2
1668
+ / (w * exx3 + e * exx2)
1669
+ / ezz4
1670
+ / ezz1
1671
+ / (w * exx4 + e * exx1)
1672
+ / (n + s)
1673
+ * exx2
1674
+ * exx3
1675
+ * n
1676
+ * exx1
1677
+ * exx4
1678
+ * s
1679
+ ) / b
1680
+
1681
+ bzyw = (
1682
+ (
1683
+ (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3)
1684
+ * (
1685
+ 0.5
1686
+ * n
1687
+ * ezz1
1688
+ * ezz2
1689
+ / eyy1
1690
+ * (
1691
+ (1 - eyy1 / ezz1) / n / w
1692
+ - eyx1 / ezz1 * (2.0 / w**2 - 2 / w**2 * e / (e + w))
1693
+ )
1694
+ - (ezz1 - ezz2) * e / w / (e + w)
1695
+ + 0.5
1696
+ * s
1697
+ * ezz2
1698
+ * ezz1
1699
+ / eyy2
1700
+ * (
1701
+ -(1 - eyy2 / ezz2) / s / w
1702
+ - eyx2 / ezz2 * (2.0 / w**2 - 2 / w**2 * e / (e + w))
1703
+ )
1704
+ )
1705
+ + (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2)
1706
+ * (
1707
+ -n * ezz3 / eyy4 * eyx4 / w / (e + w)
1708
+ - s * ezz4 / eyy3 * eyx3 / w / (e + w)
1709
+ - (ezz4 - ezz3) * e / w / (e + w)
1710
+ )
1711
+ )
1712
+ / ezz4
1713
+ / ezz3
1714
+ / (n * eyy3 + s * eyy4)
1715
+ / ezz2
1716
+ / ezz1
1717
+ / (n * eyy2 + s * eyy1)
1718
+ / (e + w)
1719
+ * eyy4
1720
+ * eyy3
1721
+ * eyy1
1722
+ * w
1723
+ * eyy2
1724
+ * e
1725
+ + (
1726
+ 0.5
1727
+ * (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e)
1728
+ * ezz4
1729
+ * (2 * exx1 / ezz1 / w**2 + exy1 / ezz1 / n / w)
1730
+ / exx1
1731
+ * ezz1
1732
+ * w
1733
+ - 0.5
1734
+ * (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e)
1735
+ * ezz3
1736
+ * (2 * exx2 / ezz2 / w**2 - exy2 / ezz2 / s / w)
1737
+ / exx2
1738
+ * ezz2
1739
+ * w
1740
+ )
1741
+ / ezz3
1742
+ / ezz2
1743
+ / (w * exx3 + e * exx2)
1744
+ / ezz4
1745
+ / ezz1
1746
+ / (w * exx4 + e * exx1)
1747
+ / (n + s)
1748
+ * exx2
1749
+ * exx3
1750
+ * n
1751
+ * exx1
1752
+ * exx4
1753
+ * s
1754
+ ) / b
1755
+
1756
+ bzyp = (
1757
+ (
1758
+ (-n * ezz4 * ezz3 / eyy4 - s * ezz3 * ezz4 / eyy3)
1759
+ * (
1760
+ 0.5
1761
+ * n
1762
+ * ezz1
1763
+ * ezz2
1764
+ / eyy1
1765
+ * (
1766
+ -(k**2) * eyx1
1767
+ - (1 - eyy1 / ezz1) / n / w
1768
+ - eyx1 / ezz1 * (-2.0 / w**2 + 2 / w**2 * (e - w) / e)
1769
+ )
1770
+ + (ezz1 - ezz2) * (e - w) / e / w
1771
+ + 0.5
1772
+ * s
1773
+ * ezz2
1774
+ * ezz1
1775
+ / eyy2
1776
+ * (
1777
+ -(k**2) * eyx2
1778
+ + (1 - eyy2 / ezz2) / s / w
1779
+ - eyx2 / ezz2 * (-2.0 / w**2 + 2 / w**2 * (e - w) / e)
1780
+ )
1781
+ )
1782
+ + (n * ezz1 * ezz2 / eyy1 + s * ezz2 * ezz1 / eyy2)
1783
+ * (
1784
+ 0.5
1785
+ * n
1786
+ * ezz4
1787
+ * ezz3
1788
+ / eyy4
1789
+ * (
1790
+ -(k**2) * eyx4
1791
+ + (1 - eyy4 / ezz4) / n / e
1792
+ - eyx4 / ezz4 * (-2.0 / e**2 - 2 / e**2 * (e - w) / w)
1793
+ )
1794
+ + 0.5
1795
+ * s
1796
+ * ezz3
1797
+ * ezz4
1798
+ / eyy3
1799
+ * (
1800
+ -(k**2) * eyx3
1801
+ - (1 - eyy3 / ezz3) / s / e
1802
+ - eyx3 / ezz3 * (-2.0 / e**2 - 2 / e**2 * (e - w) / w)
1803
+ )
1804
+ + (ezz4 - ezz3) * (e - w) / e / w
1805
+ )
1806
+ )
1807
+ / ezz4
1808
+ / ezz3
1809
+ / (n * eyy3 + s * eyy4)
1810
+ / ezz2
1811
+ / ezz1
1812
+ / (n * eyy2 + s * eyy1)
1813
+ / (e + w)
1814
+ * eyy4
1815
+ * eyy3
1816
+ * eyy1
1817
+ * w
1818
+ * eyy2
1819
+ * e
1820
+ + (
1821
+ (ezz3 / exx2 * ezz2 * w + ezz2 / exx3 * ezz3 * e)
1822
+ * (
1823
+ 0.5
1824
+ * ezz4
1825
+ * (
1826
+ -2.0 / n**2
1827
+ - 2 * exx1 / ezz1 / w**2
1828
+ + k**2 * exx1
1829
+ - exy1 / ezz1 / n / w
1830
+ )
1831
+ / exx1
1832
+ * ezz1
1833
+ * w
1834
+ + 0.5
1835
+ * ezz1
1836
+ * (
1837
+ -2.0 / n**2
1838
+ - 2 * exx4 / ezz4 / e**2
1839
+ + k**2 * exx4
1840
+ + exy4 / ezz4 / n / e
1841
+ )
1842
+ / exx4
1843
+ * ezz4
1844
+ * e
1845
+ )
1846
+ - (ezz4 / exx1 * ezz1 * w + ezz1 / exx4 * ezz4 * e)
1847
+ * (
1848
+ 0.5
1849
+ * ezz3
1850
+ * (
1851
+ -2.0 / s**2
1852
+ - 2 * exx2 / ezz2 / w**2
1853
+ + k**2 * exx2
1854
+ + exy2 / ezz2 / s / w
1855
+ )
1856
+ / exx2
1857
+ * ezz2
1858
+ * w
1859
+ + 0.5
1860
+ * ezz2
1861
+ * (
1862
+ -2.0 / s**2
1863
+ - 2 * exx3 / ezz3 / e**2
1864
+ + k**2 * exx3
1865
+ - exy3 / ezz3 / s / e
1866
+ )
1867
+ / exx3
1868
+ * ezz3
1869
+ * e
1870
+ )
1871
+ )
1872
+ / ezz3
1873
+ / ezz2
1874
+ / (w * exx3 + e * exx2)
1875
+ / ezz4
1876
+ / ezz1
1877
+ / (w * exx4 + e * exx1)
1878
+ / (n + s)
1879
+ * exx2
1880
+ * exx3
1881
+ * n
1882
+ * exx1
1883
+ * exx4
1884
+ * s
1885
+ ) / b
1886
+
1887
+ ii = numpy.arange(nx * ny).reshape(nx, ny)
1888
+
1889
+ # NORTH boundary
1890
+
1891
+ ib = ii[:, -1]
1892
+
1893
+ if boundary[0] == "S":
1894
+ sign = 1
1895
+ elif boundary[0] == "A":
1896
+ sign = -1
1897
+ elif boundary[0] == "0":
1898
+ sign = 0
1899
+ else:
1900
+ raise ValueError("unknown boundary conditions")
1901
+
1902
+ bzxs[ib] += sign * bzxn[ib]
1903
+ bzxse[ib] += sign * bzxne[ib]
1904
+ bzxsw[ib] += sign * bzxnw[ib]
1905
+ bzys[ib] -= sign * bzyn[ib]
1906
+ bzyse[ib] -= sign * bzyne[ib]
1907
+ bzysw[ib] -= sign * bzynw[ib]
1908
+
1909
+ # SOUTH boundary
1910
+
1911
+ ib = ii[:, 0]
1912
+
1913
+ if boundary[1] == "S":
1914
+ sign = 1
1915
+ elif boundary[1] == "A":
1916
+ sign = -1
1917
+ elif boundary[1] == "0":
1918
+ sign = 0
1919
+ else:
1920
+ raise ValueError("unknown boundary conditions")
1921
+
1922
+ bzxn[ib] += sign * bzxs[ib]
1923
+ bzxne[ib] += sign * bzxse[ib]
1924
+ bzxnw[ib] += sign * bzxsw[ib]
1925
+ bzyn[ib] -= sign * bzys[ib]
1926
+ bzyne[ib] -= sign * bzyse[ib]
1927
+ bzynw[ib] -= sign * bzysw[ib]
1928
+
1929
+ # EAST boundary
1930
+
1931
+ ib = ii[-1, :]
1932
+
1933
+ if boundary[2] == "S":
1934
+ sign = 1
1935
+ elif boundary[2] == "A":
1936
+ sign = -1
1937
+ elif boundary[2] == "0":
1938
+ sign = 0
1939
+ else:
1940
+ raise ValueError("unknown boundary conditions")
1941
+
1942
+ bzxw[ib] += sign * bzxe[ib]
1943
+ bzxnw[ib] += sign * bzxne[ib]
1944
+ bzxsw[ib] += sign * bzxse[ib]
1945
+ bzyw[ib] -= sign * bzye[ib]
1946
+ bzynw[ib] -= sign * bzyne[ib]
1947
+ bzysw[ib] -= sign * bzyse[ib]
1948
+
1949
+ # WEST boundary
1950
+
1951
+ ib = ii[0, :]
1952
+
1953
+ if boundary[3] == "S":
1954
+ sign = 1
1955
+ elif boundary[3] == "A":
1956
+ sign = -1
1957
+ elif boundary[3] == "0":
1958
+ sign = 0
1959
+ else:
1960
+ raise ValueError("unknown boundary conditions")
1961
+
1962
+ bzxe[ib] += sign * bzxw[ib]
1963
+ bzxne[ib] += sign * bzxnw[ib]
1964
+ bzxse[ib] += sign * bzxsw[ib]
1965
+ bzye[ib] -= sign * bzyw[ib]
1966
+ bzyne[ib] -= sign * bzynw[ib]
1967
+ bzyse[ib] -= sign * bzysw[ib]
1968
+
1969
+ # Assemble sparse matrix
1970
+
1971
+ iall = ii.flatten()
1972
+ i_s = ii[:, :-1].flatten()
1973
+ i_n = ii[:, 1:].flatten()
1974
+ i_e = ii[1:, :].flatten()
1975
+ i_w = ii[:-1, :].flatten()
1976
+ i_ne = ii[1:, 1:].flatten()
1977
+ i_se = ii[1:, :-1].flatten()
1978
+ i_sw = ii[:-1, :-1].flatten()
1979
+ i_nw = ii[:-1, 1:].flatten()
1980
+
1981
+ Izx = numpy.r_[iall, i_w, i_e, i_s, i_n, i_ne, i_se, i_sw, i_nw]
1982
+ Jzx = numpy.r_[iall, i_e, i_w, i_n, i_s, i_sw, i_nw, i_ne, i_se]
1983
+ Vzx = numpy.r_[
1984
+ bzxp[iall],
1985
+ bzxe[i_w],
1986
+ bzxw[i_e],
1987
+ bzxn[i_s],
1988
+ bzxs[i_n],
1989
+ bzxsw[i_ne],
1990
+ bzxnw[i_se],
1991
+ bzxne[i_sw],
1992
+ bzxse[i_nw],
1993
+ ]
1994
+
1995
+ Izy = numpy.r_[iall, i_w, i_e, i_s, i_n, i_ne, i_se, i_sw, i_nw]
1996
+ Jzy = numpy.r_[iall, i_e, i_w, i_n, i_s, i_sw, i_nw, i_ne, i_se] + nx * ny
1997
+ Vzy = numpy.r_[
1998
+ bzyp[iall],
1999
+ bzye[i_w],
2000
+ bzyw[i_e],
2001
+ bzyn[i_s],
2002
+ bzys[i_n],
2003
+ bzysw[i_ne],
2004
+ bzynw[i_se],
2005
+ bzyne[i_sw],
2006
+ bzyse[i_nw],
2007
+ ]
2008
+
2009
+ I = numpy.r_[Izx, Izy]
2010
+ J = numpy.r_[Jzx, Jzy]
2011
+ V = numpy.r_[Vzx, Vzy]
2012
+ B = coo_matrix((V, (I, J))).tocsr()
2013
+
2014
+ HxHy = numpy.r_[Hx, Hy]
2015
+ Hz = B * HxHy.ravel() / 1j
2016
+ Hz = Hz.reshape(Hx.shape)
2017
+
2018
+ # in xc e yc
2019
+ exx = epsxx[1:-1, 1:-1]
2020
+ exy = epsxy[1:-1, 1:-1]
2021
+ eyx = epsyx[1:-1, 1:-1]
2022
+ eyy = epsyy[1:-1, 1:-1]
2023
+ ezz = epszz[1:-1, 1:-1]
2024
+ edet = exx * eyy - exy * eyx
2025
+
2026
+ h = e.reshape(nx, ny)[:-1, :-1]
2027
+ v = n.reshape(nx, ny)[:-1, :-1]
2028
+
2029
+ # in xc e yc
2030
+ Dx = neff * EMpy.utils.centered2d(Hy) + (
2031
+ Hz[:-1, 1:] + Hz[1:, 1:] - Hz[:-1, :-1] - Hz[1:, :-1]
2032
+ ) / (2j * k * v)
2033
+ Dy = -neff * EMpy.utils.centered2d(Hx) - (
2034
+ Hz[1:, :-1] + Hz[1:, 1:] - Hz[:-1, 1:] - Hz[:-1, :-1]
2035
+ ) / (2j * k * h)
2036
+ Dz = (
2037
+ (Hy[1:, :-1] + Hy[1:, 1:] - Hy[:-1, 1:] - Hy[:-1, :-1]) / (2 * h)
2038
+ - (Hx[:-1, 1:] + Hx[1:, 1:] - Hx[:-1, :-1] - Hx[1:, :-1]) / (2 * v)
2039
+ ) / (1j * k)
2040
+
2041
+ Ex = (eyy * Dx - exy * Dy) / edet
2042
+ Ey = (exx * Dy - eyx * Dx) / edet
2043
+ Ez = Dz / ezz
2044
+
2045
+ Hzs.append(Hz)
2046
+ Exs.append(Ex)
2047
+ Eys.append(Ey)
2048
+ Ezs.append(Ez)
2049
+
2050
+ return (Hzs, Exs, Eys, Ezs)
2051
+
2052
+ def solve(self, neigs=4, tol=0, guess=None):
2053
+ """
2054
+ This function finds the eigenmodes.
2055
+
2056
+ Parameters
2057
+ ----------
2058
+ neigs : int
2059
+ number of eigenmodes to find
2060
+ tol : float
2061
+ Relative accuracy for eigenvalues.
2062
+ The default value of 0 implies machine precision.
2063
+ guess : float
2064
+ A guess for the refractive index.
2065
+ The modesolver will only finds eigenvectors with an
2066
+ effective refrative index higher than this value.
2067
+
2068
+ Returns
2069
+ -------
2070
+ self : an instance of the VFDModeSolver class
2071
+ obtain the fields of interest for specific modes using, for example:
2072
+ solver = EMpy.modesolvers.FD.VFDModeSolver(wavelength, x, y, epsf, boundary).solve()
2073
+ Ex = solver.modes[0].Ex
2074
+ Ey = solver.modes[0].Ey
2075
+ Ez = solver.modes[0].Ez
2076
+ """
2077
+
2078
+ from scipy.sparse.linalg import eigen
2079
+
2080
+ self.nmodes = neigs
2081
+ self.tol = tol
2082
+
2083
+ A = self.build_matrix()
2084
+
2085
+ if guess is not None:
2086
+ # calculate shift for eigs function
2087
+ k = 2 * numpy.pi / self.wl
2088
+ shift = (guess * k) ** 2
2089
+ else:
2090
+ shift = None
2091
+
2092
+ # Here is where the actual mode-solving takes place!
2093
+ [eigvals, eigvecs] = eigen.eigs(
2094
+ A,
2095
+ k=neigs,
2096
+ which="LR",
2097
+ tol=tol,
2098
+ ncv=10 * neigs,
2099
+ return_eigenvectors=True,
2100
+ sigma=shift,
2101
+ )
2102
+
2103
+ neffs = self.wl * numpy.sqrt(eigvals) / (2 * numpy.pi)
2104
+ Hxs = []
2105
+ Hys = []
2106
+ nx = self.nx
2107
+ ny = self.ny
2108
+ for ieig in range(neigs):
2109
+ Hxs.append(eigvecs[: nx * ny, ieig].reshape(nx, ny))
2110
+ Hys.append(eigvecs[nx * ny :, ieig].reshape(nx, ny))
2111
+
2112
+ # sort the modes
2113
+ idx = numpy.flipud(numpy.argsort(neffs))
2114
+ neffs = neffs[idx]
2115
+ tmpx = []
2116
+ tmpy = []
2117
+ for i in idx:
2118
+ tmpx.append(Hxs[i])
2119
+ tmpy.append(Hys[i])
2120
+ Hxs = tmpx
2121
+ Hys = tmpy
2122
+
2123
+ [Hzs, Exs, Eys, Ezs] = self.compute_other_fields(neffs, Hxs, Hys)
2124
+
2125
+ self.modes = []
2126
+ for (neff, Hx, Hy, Hz, Ex, Ey, Ez) in zip(neffs, Hxs, Hys, Hzs, Exs, Eys, Ezs):
2127
+ self.modes.append(
2128
+ FDMode(
2129
+ self.wl, self.x, self.y, neff, Ex, Ey, Ez, Hx, Hy, Hz
2130
+ ).normalize()
2131
+ )
2132
+
2133
+ return self
2134
+
2135
+ def save_modes_for_FDTD(self, x=None, y=None):
2136
+ for im, m in enumerate(self.modes):
2137
+ m.save_for_FDTD(str(im), x, y)
2138
+
2139
+ def __str__(self):
2140
+ descr = "Vectorial Finite Difference Modesolver\n"
2141
+ return descr
2142
+
2143
+
2144
+ class FDMode(Mode):
2145
+ def __init__(self, wl, x, y, neff, Ex, Ey, Ez, Hx, Hy, Hz):
2146
+ self.wl = wl
2147
+ self.x = x
2148
+ self.y = y
2149
+ self.neff = neff
2150
+ self.Ex = Ex
2151
+ self.Ey = Ey
2152
+ self.Ez = Ez
2153
+ self.Hx = Hx
2154
+ self.Hy = Hy
2155
+ self.Hz = Hz
2156
+
2157
+ def get_x(self, n=None):
2158
+ if n is None:
2159
+ return self.x
2160
+ return numpy.linspace(self.x[0], self.x[-1], n)
2161
+
2162
+ def get_y(self, n=None):
2163
+ if n is None:
2164
+ return self.y
2165
+ return numpy.linspace(self.y[0], self.y[-1], n)
2166
+
2167
+ def get_field(self, fname, x=None, y=None):
2168
+
2169
+ if fname == "Ex":
2170
+ f = self.Ex
2171
+ centered = True
2172
+ elif fname == "Ey":
2173
+ f = self.Ey
2174
+ centered = True
2175
+ elif fname == "Ez":
2176
+ f = self.Ez
2177
+ centered = True
2178
+ elif fname == "Hx":
2179
+ f = self.Hx
2180
+ centered = False
2181
+ elif fname == "Hy":
2182
+ f = self.Hy
2183
+ centered = False
2184
+ else: # if fname == "Hz"
2185
+ f = self.Hz
2186
+ centered = False
2187
+
2188
+ if (x is None) and (y is None):
2189
+ return f
2190
+
2191
+ if not centered:
2192
+ # magnetic fields are not centered
2193
+ x0 = self.x
2194
+ y0 = self.y
2195
+ else:
2196
+ # electric fields and intensity are centered
2197
+ x0 = EMpy.utils.centered1d(self.x)
2198
+ y0 = EMpy.utils.centered1d(self.y)
2199
+
2200
+ return EMpy.utils.interp2(x, y, x0, y0, f)
2201
+
2202
+ def intensityTETM(self, x=None, y=None):
2203
+ I_TE = self.Ex * EMpy.utils.centered2d(numpy.conj(self.Hy)) / 2.0
2204
+ I_TM = -self.Ey * EMpy.utils.centered2d(numpy.conj(self.Hx)) / 2.0
2205
+ if x is None and y is None:
2206
+ return (I_TE, I_TM)
2207
+ else:
2208
+ x0 = EMpy.utils.centered1d(self.x)
2209
+ y0 = EMpy.utils.centered1d(self.y)
2210
+ I_TE_ = EMpy.utils.interp2(x, y, x0, y0, I_TE)
2211
+ I_TM_ = EMpy.utils.interp2(x, y, x0, y0, I_TM)
2212
+ return (I_TE_, I_TM_)
2213
+
2214
+ def intensity(self, x=None, y=None):
2215
+ I_TE, I_TM = self.intensityTETM(x, y)
2216
+ return I_TE + I_TM
2217
+
2218
+ def TEfrac(self, x_=None, y_=None):
2219
+ if x_ is None:
2220
+ x = EMpy.utils.centered1d(self.x)
2221
+ else:
2222
+ x = x_
2223
+ if y_ is None:
2224
+ y = EMpy.utils.centered1d(self.y)
2225
+ else:
2226
+ y = y_
2227
+ STE, STM = self.intensityTETM(x_, y_)
2228
+ num = EMpy.utils.trapz2(numpy.abs(STE), x=x, y=y)
2229
+ den = EMpy.utils.trapz2(numpy.abs(STE) + numpy.abs(STM), x=x, y=y)
2230
+ return num / den
2231
+
2232
+ def norm(self):
2233
+ x = EMpy.utils.centered1d(self.x)
2234
+ y = EMpy.utils.centered1d(self.y)
2235
+ return numpy.sqrt(EMpy.utils.trapz2(self.intensity(), x=x, y=y))
2236
+
2237
+ def normalize(self):
2238
+ n = self.norm()
2239
+ self.Ex /= n
2240
+ self.Ey /= n
2241
+ self.Ez /= n
2242
+ self.Hx /= n
2243
+ self.Hy /= n
2244
+ self.Hz /= n
2245
+
2246
+ return self
2247
+
2248
+ def overlap(self, m, x=None, y=None):
2249
+
2250
+ x1 = EMpy.utils.centered1d(self.x)
2251
+ y1 = EMpy.utils.centered1d(self.y)
2252
+
2253
+ x2 = EMpy.utils.centered1d(m.x)
2254
+ y2 = EMpy.utils.centered1d(m.y)
2255
+
2256
+ if x is None:
2257
+ x = x2
2258
+
2259
+ if y is None:
2260
+ y = y2
2261
+
2262
+ # Interpolates m1 onto m2 grid:
2263
+ Ex1 = EMpy.utils.interp2(x, y, x1, y1, self.Ex)
2264
+ Ey1 = EMpy.utils.interp2(x, y, x1, y1, self.Ey)
2265
+ Hx2 = EMpy.utils.interp2(x, y, x2, y2, m.Hx)
2266
+ Hy2 = EMpy.utils.interp2(x, y, x2, y2, m.Hy)
2267
+
2268
+ intensity = (
2269
+ Ex1 * EMpy.utils.centered2d(numpy.conj(Hy2))
2270
+ - Ey1 * EMpy.utils.centered2d(numpy.conj(Hx2))
2271
+ ) / 2.0
2272
+
2273
+ return EMpy.utils.trapz2(intensity, x=x, y=y)
2274
+
2275
+ def get_fields_for_FDTD(self, x=None, y=None):
2276
+ """Get mode's field on a staggered grid.
2277
+
2278
+ Note: ignores some fields on the boudaries.
2279
+
2280
+ """
2281
+
2282
+ if x is None:
2283
+ x = self.x
2284
+ if y is None:
2285
+ y = self.y
2286
+
2287
+ # Ex: ignores y = 0, max
2288
+ x_Ex = EMpy.utils.centered1d(self.x)
2289
+ y_Ex = EMpy.utils.centered1d(self.y)
2290
+ x_Ex_FDTD = EMpy.utils.centered1d(x)
2291
+ y_Ex_FDTD = y[1:-1]
2292
+ Ex_FDTD = EMpy.utils.interp2(x_Ex_FDTD, y_Ex_FDTD, x_Ex, y_Ex, self.Ex)
2293
+ # Ey: ignores x = 0, max
2294
+ x_Ey = EMpy.utils.centered1d(self.x)
2295
+ y_Ey = EMpy.utils.centered1d(self.y)
2296
+ x_Ey_FDTD = x[1:-1]
2297
+ y_Ey_FDTD = EMpy.utils.centered1d(y)
2298
+ Ey_FDTD = EMpy.utils.interp2(x_Ey_FDTD, y_Ey_FDTD, x_Ey, y_Ey, self.Ey)
2299
+ # Ez: ignores x, y = 0, max
2300
+ x_Ez = EMpy.utils.centered1d(self.x)
2301
+ y_Ez = EMpy.utils.centered1d(self.y)
2302
+ x_Ez_FDTD = x[1:-1]
2303
+ y_Ez_FDTD = y[1:-1]
2304
+ Ez_FDTD = EMpy.utils.interp2(x_Ez_FDTD, y_Ez_FDTD, x_Ez, y_Ez, self.Ez)
2305
+ # Hx: ignores x = 0, max, /120pi, reverse direction
2306
+ x_Hx = self.x
2307
+ y_Hx = self.y
2308
+ x_Hx_FDTD = x[1:-1]
2309
+ y_Hx_FDTD = EMpy.utils.centered1d(y)
2310
+ Hx_FDTD = EMpy.utils.interp2(x_Hx_FDTD, y_Hx_FDTD, x_Hx, y_Hx, self.Hx) / (
2311
+ -120.0 * numpy.pi
2312
+ )
2313
+ # Hy: ignores y = 0, max, /120pi, reverse direction
2314
+ x_Hy = self.x
2315
+ y_Hy = self.y
2316
+ x_Hy_FDTD = EMpy.utils.centered1d(x)
2317
+ y_Hy_FDTD = y[1:-1]
2318
+ Hy_FDTD = EMpy.utils.interp2(x_Hy_FDTD, y_Hy_FDTD, x_Hy, y_Hy, self.Hy) / (
2319
+ -120.0 * numpy.pi
2320
+ )
2321
+ # Hz: /120pi, reverse direction
2322
+ x_Hz = self.x
2323
+ y_Hz = self.y
2324
+ x_Hz_FDTD = EMpy.utils.centered1d(x)
2325
+ y_Hz_FDTD = EMpy.utils.centered1d(y)
2326
+ Hz_FDTD = EMpy.utils.interp2(x_Hz_FDTD, y_Hz_FDTD, x_Hz, y_Hz, self.Hz) / (
2327
+ -120.0 * numpy.pi
2328
+ )
2329
+
2330
+ return (Ex_FDTD, Ey_FDTD, Ez_FDTD, Hx_FDTD, Hy_FDTD, Hz_FDTD)
2331
+
2332
+ @staticmethod
2333
+ def plot_field(x, y, field):
2334
+ try:
2335
+ import pylab
2336
+ except ImportError:
2337
+ print("no pylab installed")
2338
+ return
2339
+ pylab.hot()
2340
+ pylab.contour(x, y, numpy.abs(field.T), 16)
2341
+ pylab.axis("image")
2342
+
2343
+ def plot_Ex(self, x=None, y=None):
2344
+ if x is None:
2345
+ x = EMpy.utils.centered1d(self.x)
2346
+ if y is None:
2347
+ y = EMpy.utils.centered1d(self.y)
2348
+ Ex = self.get_field("Ex", x, y)
2349
+ self.plot_field(x, y, Ex)
2350
+
2351
+ def plot_Ey(self, x=None, y=None):
2352
+ if x is None:
2353
+ x = EMpy.utils.centered1d(self.x)
2354
+ if y is None:
2355
+ y = EMpy.utils.centered1d(self.y)
2356
+ Ey = self.get_field("Ey", x, y)
2357
+ self.plot_field(x, y, Ey)
2358
+
2359
+ def plot_Ez(self, x=None, y=None):
2360
+ if x is None:
2361
+ x = EMpy.utils.centered1d(self.x)
2362
+ if y is None:
2363
+ y = EMpy.utils.centered1d(self.y)
2364
+ Ez = self.get_field("Ez", x, y)
2365
+ self.plot_field(x, y, Ez)
2366
+
2367
+ def plot_Hx(self, x=None, y=None):
2368
+ if x is None:
2369
+ x = self.x
2370
+ if y is None:
2371
+ y = self.y
2372
+ Hx = self.get_field("Hx", x, y)
2373
+ self.plot_field(x, y, Hx)
2374
+
2375
+ def plot_Hy(self, x=None, y=None):
2376
+ if x is None:
2377
+ x = self.x
2378
+ if y is None:
2379
+ y = self.y
2380
+ Hy = self.get_field("Hy", x, y)
2381
+ self.plot_field(x, y, Hy)
2382
+
2383
+ def plot_Hz(self, x=None, y=None):
2384
+ if x is None:
2385
+ x = self.x
2386
+ if y is None:
2387
+ y = self.y
2388
+ Hz = self.get_field("Hz", x, y)
2389
+ self.plot_field(x, y, Hz)
2390
+
2391
+ def plot_intensity(self):
2392
+ x = EMpy.utils.centered1d(self.x)
2393
+ y = EMpy.utils.centered1d(self.y)
2394
+ I = self.intensity(x, y)
2395
+ self.plot_field(x, y, I)
2396
+
2397
+ def plot(self):
2398
+ """Plot the mode's fields."""
2399
+ try:
2400
+ import pylab
2401
+ except ImportError:
2402
+ print("no pylab installed")
2403
+ return
2404
+ pylab.figure()
2405
+ pylab.subplot(2, 3, 1)
2406
+ self.plot_Ex()
2407
+ pylab.title("Ex")
2408
+ pylab.subplot(2, 3, 2)
2409
+ self.plot_Ey()
2410
+ pylab.title("Ey")
2411
+ pylab.subplot(2, 3, 3)
2412
+ self.plot_Ez()
2413
+ pylab.title("Ez")
2414
+ pylab.subplot(2, 3, 4)
2415
+ self.plot_Hx()
2416
+ pylab.title("Hx")
2417
+ pylab.subplot(2, 3, 5)
2418
+ self.plot_Hy()
2419
+ pylab.title("Hy")
2420
+ pylab.subplot(2, 3, 6)
2421
+ self.plot_Hz()
2422
+ pylab.title("Hz")
2423
+
2424
+
2425
+ def stretchmesh(x, y, nlayers, factor, method="PPPP"):
2426
+
2427
+ # OKKIO: check me!
2428
+
2429
+ # This function can be used to continuously stretch the grid
2430
+ # spacing at the edges of the computation window for
2431
+ # finite-difference calculations. This is useful when you would
2432
+ # like to increase the size of the computation window without
2433
+ # increasing the total number of points in the computational
2434
+ # domain. The program implements four different expansion
2435
+ # methods: uniform, linear, parabolic (the default) and
2436
+ # geometric. The first three methods also allow for complex
2437
+ # coordinate stretching, which is useful for creating
2438
+ # perfectly-matched non-reflective boundaries.
2439
+ #
2440
+ # USAGE:
2441
+ #
2442
+ # [x,y] = stretchmesh(x,y,nlayers,factor);
2443
+ # [x,y] = stretchmesh(x,y,nlayers,factor,method);
2444
+ # [x,y,xc,yc] = stretchmesh(x,y,nlayers,factor);
2445
+ # [x,y,xc,yc] = stretchmesh(x,y,nlayers,factor,method);
2446
+ # [x,y,xc,yc,dx,dy] = stretchmesh(x,y,nlayers,factor);
2447
+ # [x,y,xc,yc,dx,dy] = stretchmesh(x,y,nlayers,factor,method);
2448
+ #
2449
+ # INPUT:
2450
+ #
2451
+ # x,y - vectors that specify the vertices of the original
2452
+ # grid, which are usually linearly spaced.
2453
+ # nlayers - vector that specifies how many layers of the grid
2454
+ # you would like to expand:
2455
+ # nlayers(1) = # of layers on the north boundary to stretch
2456
+ # nlayers(2) = # of layers on the south boundary to stretch
2457
+ # nlayers(3) = # of layers on the east boundary to stretch
2458
+ # nlayers(4) = # of layers on the west boundary to stretch
2459
+ # factor - cumulative factor by which the layers are to be
2460
+ # expanded. As with nlayers, this can be a 4-vector.
2461
+ # method - 4-letter string specifying the method of
2462
+ # stretching for each of the four boundaries. Four different
2463
+ # methods are supported: uniform, linear, parabolic (default)
2464
+ # and geometric. For example, method = 'LLLG' will use linear
2465
+ # expansion for the north, south and east boundaries and
2466
+ # geometric expansion for the west boundary.
2467
+ #
2468
+ # OUTPUT:
2469
+ #
2470
+ # x,y - the vertices of the new stretched grid
2471
+ # xc,yc (optional) - the center cell coordinates of the
2472
+ # stretched grid
2473
+ # dx,dy (optional) - the grid spacing (dx = diff(x))
2474
+
2475
+ xx = x.astype(complex)
2476
+ yy = y.astype(complex)
2477
+
2478
+ nlayers *= numpy.ones(4, dtype=int)
2479
+ factor *= numpy.ones(4)
2480
+
2481
+ for idx, (n, f, m) in enumerate(zip(nlayers, factor, method.upper())):
2482
+
2483
+ if n > 0 and f != 1:
2484
+
2485
+ if idx == 0:
2486
+ # north boundary
2487
+ kv = numpy.arange(len(y) - 1 - n, len(y))
2488
+ z = yy
2489
+ q1 = z[-1 - n]
2490
+ q2 = z[-1]
2491
+ elif idx == 1:
2492
+ # south boundary
2493
+ kv = numpy.arange(0, n)
2494
+ z = yy
2495
+ q1 = z[n]
2496
+ q2 = z[0]
2497
+ elif idx == 2:
2498
+ # east boundary
2499
+ kv = numpy.arange(len(x) - 1 - n, len(x))
2500
+ z = xx
2501
+ q1 = z[-1 - n]
2502
+ q2 = z[-1]
2503
+ else: # if idx == 3
2504
+ # west boundary
2505
+ kv = numpy.arange(0, n)
2506
+ z = xx
2507
+ q1 = z[n]
2508
+ q2 = z[0]
2509
+
2510
+ kv = kv.astype(int)
2511
+
2512
+ if m == "U":
2513
+ c = numpy.polyfit([q1, q2], [q1, q1 + f * (q2 - q1)], 1)
2514
+ z[kv] = numpy.polyval(c, z[kv])
2515
+ elif m == "L":
2516
+ c = (f - 1) / (q2 - q1)
2517
+ b = 1 - 2 * c * q1
2518
+ a = q1 - b * q1 - c * q1**2
2519
+ z[kv] = a + b * z[kv] + c * z[kv] ** 2
2520
+ elif m == "P":
2521
+ z[kv] = z[kv] + (f - 1) * (z[kv] - q1) ** 3 / (q2 - q1) ** 2
2522
+ elif m == "G":
2523
+ b = scipy.optimize.newton(lambda s: numpy.exp(s) - 1 - f * s, f)
2524
+ a = (q2 - q1) / b
2525
+ z[kv] = q1 + a * (numpy.exp((z[kv] - q1) / a) - 1)
2526
+
2527
+ xx = xx.real + 1j * numpy.abs(xx.imag)
2528
+ yy = yy.real + 1j * numpy.abs(yy.imag)
2529
+
2530
+ xc = (xx[:-1] + xx[1:]) / 2.0
2531
+ yc = (yy[:-1] + yy[1:]) / 2.0
2532
+
2533
+ dx = numpy.diff(xx)
2534
+ dy = numpy.diff(yy)
2535
+
2536
+ return (xx, yy, xc, yc, dx, dy)