[fdfd.eme] Add semi-analytic taper approximation
This commit is contained in:
parent
bf99f35f9b
commit
7e6363ea04
6 changed files with 541 additions and 1 deletions
134
examples/eme_taper_cmt.py
Normal file
134
examples/eme_taper_cmt.py
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
"""
|
||||
Local-mode CMT example for a continuously varying rib-waveguide taper.
|
||||
|
||||
This example keeps geometry construction outside `meanas.fdfd.eme`: it samples a
|
||||
width taper at several cross-sections, solves and normalizes each local mode with
|
||||
`waveguide_2d`, then asks `eme.get_taper_s(...)` for the bidirectional taper
|
||||
scattering matrix.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy
|
||||
from numpy import pi
|
||||
|
||||
from meanas.fdmath import vec
|
||||
from meanas.fdfd import eme, waveguide_2d
|
||||
|
||||
|
||||
WL = 1310.0
|
||||
DX = 80.0
|
||||
TAPER_LENGTH = 4e3
|
||||
WIDTH_LEFT = 280.0
|
||||
WIDTH_RIGHT = 520.0
|
||||
THF = 160.0
|
||||
THP = 80.0
|
||||
EPS_SI = 3.51 ** 2
|
||||
EPS_OX = 1.453 ** 2
|
||||
MODE_NUMBERS = numpy.array([0])
|
||||
N_SECTIONS = 7
|
||||
|
||||
|
||||
def build_dxes(shape: tuple[int, int], dx: float = DX) -> list[list[numpy.ndarray]]:
|
||||
nx, ny = shape
|
||||
return [
|
||||
[numpy.full(nx, dx), numpy.full(ny, dx)],
|
||||
[numpy.full(nx, dx), numpy.full(ny, dx)],
|
||||
]
|
||||
|
||||
|
||||
def build_cross_section(
|
||||
*,
|
||||
width: float,
|
||||
x: numpy.ndarray,
|
||||
y: numpy.ndarray,
|
||||
eps_si: float = EPS_SI,
|
||||
eps_ox: float = EPS_OX,
|
||||
thf: float = THF,
|
||||
thp: float = THP,
|
||||
) -> numpy.ndarray:
|
||||
epsilon = numpy.full((3, x.size, y.size), eps_ox, dtype=float)
|
||||
xx = x[:, None]
|
||||
yy = y[None, :]
|
||||
slab = (yy >= 0) & (yy <= thp)
|
||||
rib = (abs(xx) <= width / 2) & (yy >= 0) & (yy <= thf)
|
||||
epsilon[:, slab.repeat(x.size, axis=0)] = eps_si
|
||||
epsilon[:, rib] = eps_si
|
||||
return epsilon
|
||||
|
||||
|
||||
def solve_cross_section_modes(
|
||||
epsilon: numpy.ndarray,
|
||||
*,
|
||||
omega: float,
|
||||
dxes_2d: list[list[numpy.ndarray]],
|
||||
mode_numbers: numpy.ndarray = MODE_NUMBERS,
|
||||
) -> tuple[list[tuple[numpy.ndarray, numpy.ndarray]], numpy.ndarray]:
|
||||
epsilon_vec = vec(epsilon)
|
||||
e_xys, wavenumbers = waveguide_2d.solve_modes(
|
||||
epsilon=epsilon_vec,
|
||||
omega=omega,
|
||||
dxes=dxes_2d,
|
||||
mode_numbers=mode_numbers,
|
||||
)
|
||||
eh_fields = [
|
||||
waveguide_2d.normalized_fields_e(
|
||||
e_xy,
|
||||
wavenumber=wavenumber,
|
||||
dxes=dxes_2d,
|
||||
omega=omega,
|
||||
epsilon=epsilon_vec,
|
||||
)
|
||||
for e_xy, wavenumber in zip(e_xys, wavenumbers, strict=True)
|
||||
]
|
||||
return eh_fields, wavenumbers
|
||||
|
||||
|
||||
def solve_taper_sections() -> tuple[list[eme.ModeSection], list[float], float, list[list[numpy.ndarray]]]:
|
||||
omega = 2 * pi / WL
|
||||
x = numpy.arange(-480, 480 + DX, DX)
|
||||
y = numpy.arange(-240, 400 + DX, DX)
|
||||
dxes_2d = build_dxes((x.size, y.size))
|
||||
z_samples = numpy.linspace(0, TAPER_LENGTH, N_SECTIONS)
|
||||
widths = numpy.linspace(WIDTH_LEFT, WIDTH_RIGHT, N_SECTIONS)
|
||||
|
||||
sections = []
|
||||
neffs = []
|
||||
for z_coord, width in zip(z_samples, widths, strict=True):
|
||||
epsilon = build_cross_section(width=float(width), x=x, y=y)
|
||||
modes, wavenumbers = solve_cross_section_modes(epsilon, omega=omega, dxes_2d=dxes_2d)
|
||||
sections.append(eme.ModeSection(float(z_coord), modes, wavenumbers))
|
||||
neffs.append(float(numpy.real(wavenumbers[0] / omega)))
|
||||
|
||||
return sections, neffs, omega, dxes_2d
|
||||
|
||||
|
||||
def print_summary(
|
||||
taper_s: numpy.ndarray,
|
||||
abrupt_s: numpy.ndarray,
|
||||
neffs: list[float],
|
||||
) -> None:
|
||||
n_modes = len(MODE_NUMBERS)
|
||||
print('sampled taper effective indices:', ', '.join(f'{value:.5f}' for value in neffs))
|
||||
print(f'abrupt endpoint reflection |S_00|^2 = {abs(abrupt_s[0, 0]) ** 2:.6f}')
|
||||
print(f'abrupt endpoint transmission |S_{n_modes},0|^2 = {abs(abrupt_s[n_modes, 0]) ** 2:.6f}')
|
||||
print(f'taper CMT reflection |S_00|^2 = {abs(taper_s[0, 0]) ** 2:.6f}')
|
||||
print(f'taper CMT transmission |S_{n_modes},0|^2 = {abs(taper_s[n_modes, 0]) ** 2:.6f}')
|
||||
print(f'taper CMT total output power = {numpy.sum(abs(taper_s[:, 0]) ** 2):.6f}')
|
||||
|
||||
|
||||
def main() -> None:
|
||||
sections, neffs, _omega, dxes_2d = solve_taper_sections()
|
||||
taper_s = eme.get_taper_s(sections, dxes=dxes_2d)
|
||||
abrupt_s = eme.get_s(
|
||||
sections[0].modes,
|
||||
sections[0].wavenumbers,
|
||||
sections[-1].modes,
|
||||
sections[-1].wavenumbers,
|
||||
dxes=dxes_2d,
|
||||
)
|
||||
print_summary(taper_s, abrupt_s, neffs)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue