File size: 4,484 Bytes
fa34304 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
import numpy as np
import warnings
def slab_mode_source(x, w, n_WG, n0, wavelength, ind_m=0, x0=0):
"""
Returns the normalized TE mode profile for a symmetric slab waveguide with a lateral shift x0.
"""
k0 = 2 * np.pi / wavelength
def f_even(beta):
if beta < n0*k0 or beta > n_WG*k0:
return None
inside = n_WG**2 * k0**2 - beta**2
outside = beta**2 - n0**2 * k0**2
if inside <= 0 or outside <= 0:
return None
kx = np.sqrt(inside)
kappa = np.sqrt(outside)
return kx * np.tan(kx * w / 2) - kappa
def f_odd(beta):
if beta < n0*k0 or beta > n_WG*k0:
return None
inside = n_WG**2 * k0**2 - beta**2
outside = beta**2 - n0**2 * k0**2
if inside <= 0 or outside <= 0:
return None
kx = np.sqrt(inside)
kappa = np.sqrt(outside)
sin_term = np.sin(kx * w / 2)
if abs(sin_term) < 1e-12:
return None
return - kx * (np.cos(kx * w / 2) / sin_term) - kappa
def valid_even(beta):
inside = n_WG**2 * k0**2 - beta**2
if inside <= 0:
return False
kx = np.sqrt(inside)
theta = kx * w / 2
m = int(np.floor(2 * theta / np.pi))
if m % 2 == 0:
if m == 0 and theta > (np.pi/2 - 0.1):
return False
return True
return False
def valid_odd(beta):
inside = n_WG**2 * k0**2 - beta**2
if inside <= 0:
return False
kx = np.sqrt(inside)
theta = kx * w / 2
m = int(np.floor(2 * theta / np.pi))
return (m % 2 == 1)
N = 2000
beta_scan = np.linspace(n0*k0, n_WG*k0, N)
even_intervals = []
odd_intervals = []
f_even_vals = [f_even(b) for b in beta_scan]
f_odd_vals = [f_odd(b) for b in beta_scan]
for i in range(N-1):
if (f_even_vals[i] is not None) and (f_even_vals[i+1] is not None):
if f_even_vals[i] * f_even_vals[i+1] < 0:
even_intervals.append((beta_scan[i], beta_scan[i+1]))
if (f_odd_vals[i] is not None) and (f_odd_vals[i+1] is not None):
if f_odd_vals[i] * f_odd_vals[i+1] < 0:
odd_intervals.append((beta_scan[i], beta_scan[i+1]))
def refine_root(f, b_left, b_right):
for _ in range(50):
b_mid = 0.5*(b_left+b_right)
val_mid = f(b_mid)
if val_mid is None:
b_right = b_mid
continue
if abs(val_mid) < 1e-9:
return b_mid
val_left = f(b_left)
if val_left is None or val_left*val_mid > 0:
b_left = b_mid
else:
b_right = b_mid
return b_mid
even_roots = []
for (b_left, b_right) in even_intervals:
root = refine_root(f_even, b_left, b_right)
if valid_even(root):
even_roots.append(root)
odd_roots = []
for (b_left, b_right) in odd_intervals:
root = refine_root(f_odd, b_left, b_right)
if valid_odd(root):
odd_roots.append(root)
modes = [("even", r) for r in even_roots] + [("odd", r) for r in odd_roots]
modes_sorted = sorted(modes, key=lambda tup: tup[1], reverse=True)
if len(modes_sorted) == 0:
raise ValueError("No guided slab modes found in [n0*k0, n_WG*k0].")
if ind_m >= len(modes_sorted):
warnings.warn(
f"Requested mode index {ind_m} >= found modes ({len(modes_sorted)}). Using highest mode index {len(modes_sorted)-1}.",
UserWarning
)
ind_m = len(modes_sorted) - 1
parity, beta_chosen = modes_sorted[ind_m]
inside = n_WG**2 * k0**2 - beta_chosen**2
outside = beta_chosen**2 - n0**2 * k0**2
kx = np.sqrt(inside)
kappa = np.sqrt(outside)
E = np.zeros_like(x, dtype=np.complex128)
if parity == "even":
for i, xi in enumerate(x):
xp = xi - x0
if abs(xp) <= w/2:
E[i] = np.cos(kx * xp)
else:
E[i] = np.cos(kx * (w/2)) * np.exp(-kappa * (abs(xp)-w/2))
else:
for i, xi in enumerate(x):
xp = xi - x0
if abs(xp) <= w/2:
E[i] = np.sin(kx * xp)
else:
E[i] = np.sign(xp) * np.sin(kx * (w/2)) * np.exp(-kappa * (abs(xp)-w/2))
norm = np.sqrt(np.trapz(np.abs(E)**2, x))
E /= norm
return E
|