BPM / app.py
jwt625's picture
add MMI
b53e7e7
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import tempfile
import gradio as gr
from bpm.refractive_index import (
generate_waveguide_n_r2,
generate_MMI_n_r2
)
from bpm.mode_solver import slab_mode_source
from bpm.core import run_bpm
from bpm.pml import generate_sigma_x
def run_waveguide(w, l, L_bend, n_WG, wavelength, ind_m):
# --- Simulation setup (Waveguide S-bend) ---
domain_size = 50.0
z_total = 500.0
Nx, Nz = 256, 2000
x = np.linspace(-domain_size/2, domain_size/2, Nx)
z = np.linspace(0, z_total, Nz)
n0 = 1.0
# Refractive index map
n_r2 = generate_waveguide_n_r2(x, z, l, L_bend, w, n_WG, n0)
# Launch slab mode
E0 = slab_mode_source(x, w, n_WG, n0, wavelength, ind_m, x0=0)
E = np.zeros((Nx, Nz), dtype=np.complex128)
E[:, 0] = E0
# PML + BPM
dx = domain_size / Nx
dz = z[1] - z[0]
sigma_x = generate_sigma_x(x, dx, wavelength, domain_size)
E_out = run_bpm(E, n_r2, x, z, dx, dz, n0, sigma_x, wavelength)
# Plot
fig, ax = plt.subplots(figsize=(8,6))
im = ax.imshow(
np.abs(E_out)**2,
extent=[x[0], x[-1], z[0], z[-1]],
origin='lower',
aspect='auto',
cmap='inferno'
)
ax.set_title("Waveguide S-bend BPM Propagation")
ax.set_xlabel("x (µm)")
ax.set_ylabel("z (µm)")
fig.colorbar(im, ax=ax, label="Intensity")
# Save data for download
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".npz")
np.savez(tmp.name, E_out=E_out, x=x, z=z)
tmp.close()
return fig, tmp.name
def run_mmi(z_start, L_MMI, w_MMI, w_wg, d, n_WG, wavelength, ind_m):
# --- Simulation setup (MMI Splitter) ---
domain_size = 50.0
z_total = 250.0
Nx, Nz = 256, 1024
x = np.linspace(-domain_size/2, domain_size/2, Nx)
z = np.linspace(0, z_total, Nz)
n0 = 1.0
# For simplicity, use the same core and MMI index
n_MMI = n_WG
# Refractive index map
n_r2 = generate_MMI_n_r2(
x, z,
z_MMI_start = z_start,
L_MMI = L_MMI,
w_MMI = w_MMI,
w_wg = w_wg,
d = d,
n_WG = n_WG,
n_MMI = n_MMI,
n0 = n0
)
# Launch slab mode in left waveguide
E0 = slab_mode_source(x, w_wg, n_WG, n0, wavelength, ind_m, x0=-d/2)
E = np.zeros((Nx, Nz), dtype=np.complex128)
E[:,0] = E0
# PML + BPM
dx = domain_size / Nx
dz = z[1] - z[0]
sigma_x = generate_sigma_x(x, dx, wavelength, domain_size)
E_out = run_bpm(E, n_r2, x, z, dx, dz, n0, sigma_x, wavelength)
# Plot
fig, ax = plt.subplots(figsize=(8,6))
im = ax.imshow(
np.abs(E_out)**2,
extent=[x[0], x[-1], z[0], z[-1]],
origin='lower',
aspect='auto',
cmap='inferno'
)
ax.set_title("MMI Splitter BPM Propagation")
ax.set_xlabel("x (µm)")
ax.set_ylabel("z (µm)")
fig.colorbar(im, ax=ax, label="Intensity")
# Save data
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".npz")
np.savez(tmp.name, E_out=E_out, x=x, z=z)
tmp.close()
return fig, tmp.name
def run_dispatcher(
sim_type,
# waveguide args
w, l, L_bend,
# mmi args
z_start, L_MMI, w_MMI, w_wg, d,
# common args
n_WG, wavelength, ind_m
):
if sim_type == "Waveguide S-bend":
return run_waveguide(w, l, L_bend, n_WG, wavelength, ind_m)
else:
return run_mmi(z_start, L_MMI, w_MMI, w_wg, d, n_WG, wavelength, ind_m)
with gr.Blocks() as demo:
gr.Markdown("## BPM Simulation Dashboard")
# 1) Simulation selector
sim_type = gr.Radio(
choices=["Waveguide S-bend", "MMI Splitter"],
value="Waveguide S-bend",
label="Simulation Type"
)
with gr.Row():
# 2) Waveguide parameters panel
with gr.Column(visible=True) as waveguide_panel:
w_slider = gr.Slider(0.1, 5.0, value=1.0, step=0.1, label="Waveguide width w (µm)")
l_slider = gr.Slider(0.0, 10.0, value=5.0, step=0.1, label="Lateral offset l (µm)")
Lb_slider = gr.Slider(50.0, 500.0,value=200.0,step=10.0,label="S-bend length L (µm)")
# 3) MMI parameters panel
with gr.Column(visible=False) as mmi_panel:
z0_slider = gr.Slider(0.0, 200.0, value=50.0, step=1.0, label="MMI start z₀ (µm)")
Lmmi_slider = gr.Slider(10.0, 300.0,value=130.0,step=5.0, label="MMI length L (µm)")
wmmi_slider = gr.Slider(2.0, 20.0, value=8.0, step=0.5, label="MMI width w_MMI (µm)")
wwg_slider = gr.Slider(0.5, 5.0, value=2.0, step=0.1, label="I/O waveguide width w_wg (µm)")
d_slider = gr.Slider(1.0, 20.0, value=4.0, step=0.5, label="Waveguide separation d (µm)")
# 4) Common parameters
with gr.Column(scale=1):
n_WG_slider = gr.Slider(1.0, 2.0, value=1.1, step=0.01, label="Core refractive index n_WG")
wavelength_slider= gr.Slider(0.4, 1.6, value=0.532,step=0.01, label="Wavelength λ (µm)")
ind_m_slider = gr.Slider(0, 4, value=0, step=1, label="Mode index ind_m")
run_button = gr.Button("Run BPM")
download_button = gr.DownloadButton(label="Download data")
# 5) Plot output
with gr.Column(scale=2):
plot_output = gr.Plot()
# 6) Toggle panels on sim_type change
sim_type.change(
fn=lambda choice: (
gr.update(visible=(choice=="Waveguide S-bend")),
gr.update(visible=(choice=="MMI Splitter"))
),
inputs=sim_type,
outputs=[waveguide_panel, mmi_panel]
)
# 7) Wire up Run button + auto-update
all_inputs = [
sim_type,
w_slider, l_slider, Lb_slider,
z0_slider, Lmmi_slider, wmmi_slider, wwg_slider, d_slider,
n_WG_slider, wavelength_slider, ind_m_slider
]
run_button.click(
fn=run_dispatcher,
inputs=all_inputs,
outputs=[plot_output, download_button]
)
for inp in all_inputs:
inp.change(
fn=run_dispatcher,
inputs=all_inputs,
outputs=[plot_output, download_button]
)
demo.launch()