{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "160032ce",
   "metadata": {},
   "source": [
    "# Build a simple waveguide bend simulator GUI"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6a34241",
   "metadata": {},
   "source": [
    "### A case study of a 90-degree waveguide bend simulator "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1c2d627",
   "metadata": {},
   "source": [
    "`Tidy3D` offers a versatile web-based [graphical user interface](https://tidy3d.simulation.cloud/) (GUI) suitable for a wide range of FDTD simulations from photonic integrated circuit components to metasurfaces and diffractive gratings. While this general-purpose GUI is beneficial for its extensive capabilities, there are situations where you might need to focus on simulating a specific device type repeatedly. For these scenarios, Tidy3D's [Python API](https://docs.flexcompute.com/projects/tidy3d/en/latest/api/index.html) paired with open-source Python libraries such as [tkinter](https://docs.python.org/3/library/tkinter.html) enables the easy creation of a customized GUI. This specialized interface, designed with fewer controls and buttons, streamlines the simulation process for ease of use. By sharing this bespoke GUI with less experienced colleagues, they can also efficiently run simulations without extensive prior knowledge or expertise in FDTD.\n",
    "\n",
    "Here we demonstrate the creation of a simple GUI for 90-degree waveguide bend simulation. In this GUI, users can select materials for the waveguide bend, substrate, and cladding through combo boxes. We include common materials such as silicon, silicon nitride, silicon oxide, and so on. Users can also select between a circular bend and an Euler bend. Geometric parameters can be entered in the input boxes to define the waveguide dimensions and bend radius. Before running the simulation, users can view the simulation setup as well as the grid by clicking the \"View simulation\" button. Lastly, users can start the simulation by clicking the \"Run simulation\" button. After the simulation, the bending loss is automatically plotted. We also include a text window to display some relevant text outputs. \n",
    "\n",
    "As a tutorial, we use Jupyter Notebook to store the codes for the GUI creation. If you run the notebook, the GUI window will appear and you can interact with it. One can further package this GUI into a stand-alone application for easier distribution if needed. The GUI should look like below after launching it:\n",
    "\n",
    "<br>\n",
    "<br>\n",
    "\n",
    "<center><img src=\"img/tkinter_gui.png\" alt=\"diagram\" width=\"700\"/></center>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2922a07e",
   "metadata": {},
   "source": [
    "To run this notebook successfully, one needs to have the `tkinter`, `gdstk`, and other relevant libraries installed. First, we perform the necessary Python library imports."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "6bc76f3b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import tkinter as tk\n",
    "from tkinter import ttk\n",
    "\n",
    "import gdstk\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import scipy.integrate as integrate\n",
    "import tidy3d as td\n",
    "import tidy3d.web as web\n",
    "from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg\n",
    "from scipy.optimize import fsolve"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "38be733a",
   "metadata": {},
   "source": [
    "Define basic parameters. These parameters are not meant to be changed by the GUI user."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "69c3a17c",
   "metadata": {},
   "outputs": [],
   "source": [
    "num_freq = 30  # number of frequency points\n",
    "num_theta = 100  # number of circular bend points\n",
    "nm_to_um = 1e-3  # conversion factor from nm to um\n",
    "min_steps_per_wvl = 15  # grid resolution\n",
    "\n",
    "# euler bend parameters\n",
    "R_eff = 4\n",
    "A = 2.4"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17ede518",
   "metadata": {},
   "source": [
    "We put the supported media into a dictionary for easier mapping later on. In this specific GUI, we include a few common materials such as silicon, silicon dioxide, silicon nitride, and so on. More can be included as needed. \n",
    "\n",
    "Two waveguide bend shapes are included: circular or Euler. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "47dc1985",
   "metadata": {},
   "outputs": [],
   "source": [
    "# dictionary to store all materials\n",
    "media = {\n",
    "    \"Si\": td.material_library[\"cSi\"][\"Palik_Lossless\"],\n",
    "    \"SiO2\": td.material_library[\"SiO2\"][\"Palik_Lossless\"],\n",
    "    \"Si3N4\": td.material_library[\"Si3N4\"][\"Luke2015PMLStable\"],\n",
    "    \"Air\": td.Medium(permittivity=1),\n",
    "    \"PMMA\": td.material_library[\"PMMA\"][\"Horiba\"],\n",
    "    \"InAs\": td.material_library[\"InAs\"][\"Palik\"],\n",
    "    \"GaAs\": td.material_library[\"GaAs\"][\"Palik_Lossy\"],\n",
    "}\n",
    "\n",
    "materials = list(media.keys())  # list of materials for the combo box\n",
    "shapes = [\"Circular\", \"Euler\"]  # list of shapes for the combo box"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "92e23967",
   "metadata": {},
   "source": [
    "Next we define functions to compute the coordinates for the circular and Euler bends. Many codes here are adapted from our previous example on [Euler waveguide bends](https://www.flexcompute.com/tidy3d/examples/notebooks/EulerWaveguideBend/)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ae2f5f06",
   "metadata": {},
   "outputs": [],
   "source": [
    "# function to calculate the coordinates for a circular bend\n",
    "def circular_xy(r):\n",
    "    theta = np.linspace(0, np.pi / 2, num_theta)\n",
    "    x_circle = r * np.sin(theta)\n",
    "    y_circle = r - r * np.cos(theta)\n",
    "    return x_circle, y_circle\n",
    "\n",
    "\n",
    "# function to calculate the coordinates for an Euler bend\n",
    "def euler_xy(r):\n",
    "    L_max = 0  # starting point of L_max\n",
    "    precision = 0.05  # increment of L_max at each iteration\n",
    "    tolerance = 0.01  # difference tolerance of the derivatives\n",
    "\n",
    "    # determine L_max\n",
    "    while True:\n",
    "        L_max = L_max + precision  # update L_max\n",
    "        Ls = np.linspace(0, L_max, 50)  # L at (x1,y1)\n",
    "        x1 = np.zeros(len(Ls))  # x coordinate of the clothoid curve\n",
    "        y1 = np.zeros(len(Ls))  # y coordinate of the clothoid curve\n",
    "\n",
    "        # compute x1 and y1 using the above integral equations\n",
    "        for i, L in enumerate(Ls):\n",
    "            y1[i], err = integrate.quad(lambda theta: A * np.sin(theta**2 / 2), 0, L / A)\n",
    "            x1[i], err = integrate.quad(lambda theta: A * np.cos(theta**2 / 2), 0, L / A)\n",
    "\n",
    "        # compute the derivative at L_max\n",
    "        k = -(x1[-1] - x1[-2]) / (y1[-1] - y1[-2])\n",
    "        xp = x1[-1]\n",
    "        yp = y1[-1]\n",
    "        # check if the derivative is continuous at L_max\n",
    "        R = np.sqrt(\n",
    "            ((R_eff + k * xp - yp) / (k + 1) - xp) ** 2\n",
    "            + (-(R_eff + k * xp - yp) / (k + 1) + R_eff - yp) ** 2\n",
    "        )\n",
    "        if np.abs(R - A**2 / L_max) < tolerance:\n",
    "            break\n",
    "\n",
    "    # after L_max is determined, R_min is also determined\n",
    "    R_min = A**2 / L_max\n",
    "\n",
    "    x3 = np.flipud(R_eff - y1)\n",
    "    y3 = np.flipud(R_eff - x1)\n",
    "\n",
    "    # solve for the parameters of the circular curve\n",
    "    def circle(var):\n",
    "        a = var[0]\n",
    "        b = var[1]\n",
    "        Func = np.empty(2)\n",
    "        Func[0] = (xp - a) ** 2 + (yp - b) ** 2 - R_min**2\n",
    "        Func[1] = (R_eff - yp - a) ** 2 + (R_eff - xp - b) ** 2 - R_min**2\n",
    "        return Func\n",
    "\n",
    "    a, b = fsolve(circle, (0, R_eff))\n",
    "\n",
    "    # calculate the coordinates of the circular curve\n",
    "    x2 = np.linspace(xp + 0.01, R_eff - yp - 0.01, 50)\n",
    "    y2 = -np.sqrt(R_min**2 - (x2 - a) ** 2) + b\n",
    "\n",
    "    x_euler = np.concatenate((x1, x2, x3))\n",
    "    y_euler = np.concatenate((y1, y2, y3))\n",
    "\n",
    "    return r * x_euler / R_eff, r * y_euler / R_eff"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f8be7e1",
   "metadata": {},
   "source": [
    "We also create a helper function to convert the $x$ and $y$ coordinates of a path into a Tidy3D [Structure](https://docs.flexcompute.com/projects/tidy3d/en/latest/api/_autosummary/tidy3d.Structure.html) by utilizing [gdstk](https://heitzmann.github.io/gdstk/)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "82fbfdb5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# function to convert the coordinates of a path into a bend structure\n",
    "def line_to_structure(x, y, r, t, w, wg_mat):\n",
    "    cell = gdstk.Cell(\"bend\")  # define a gds cell\n",
    "    inf_eff = 2 * r\n",
    "    # add points to include the input and output straight waveguides\n",
    "    x = np.insert(x, 0, -inf_eff)\n",
    "    x = np.append(x, r)\n",
    "    y = np.insert(y, 0, 0)\n",
    "    y = np.append(y, inf_eff)\n",
    "\n",
    "    cell.add(gdstk.FlexPath(x + 1j * y, w, layer=1, datatype=0))  # add path to cell\n",
    "\n",
    "    # define structure from cell\n",
    "    bend = td.Structure(\n",
    "        geometry=td.PolySlab.from_gds(\n",
    "            cell,\n",
    "            gds_layer=1,\n",
    "            axis=2,\n",
    "            slab_bounds=(0, t),\n",
    "        )[0],\n",
    "        medium=media[wg_mat],\n",
    "    )\n",
    "    return bend"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "853e2641",
   "metadata": {},
   "source": [
    "Next we define a function to create a Tidy3D [Simulation](https://docs.flexcompute.com/projects/tidy3d/en/latest/api/_autosummary/tidy3d.Simulation.html) instance. In this function, we need to extract the values of the user inputs in the GUI, define the waveguide bend structure based on the extracted values, define a [ModeSource](https://docs.flexcompute.com/projects/tidy3d/en/latest/api/_autosummary/tidy3d.ModeSource.html) to excite the input waveguide and a [ModeMonitor](https://docs.flexcompute.com/projects/tidy3d/en/latest/api/_autosummary/tidy3d.ModeMonitor.html) to measure the transmission to the output waveguide, and lastly return a [Simulation](https://docs.flexcompute.com/projects/tidy3d/en/latest/api/_autosummary/tidy3d.Simulation.html)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "07b30437",
   "metadata": {},
   "outputs": [],
   "source": [
    "# function to compute wavelength range\n",
    "def get_ldas():\n",
    "    lda0 = float(lda0_box.get()) * nm_to_um\n",
    "    bw = float(bw_box.get()) * nm_to_um\n",
    "    ldas = np.linspace(lda0 - 0.5 * bw, lda0 + 0.5 * bw, num_freq)\n",
    "    return lda0, ldas\n",
    "\n",
    "\n",
    "# function to define the bend simulation\n",
    "def make_sim():\n",
    "    # get user input values from the gui\n",
    "    sub_mat = substrate_box.get()\n",
    "    wg_mat = wg_box.get()\n",
    "    clad_mat = cladding_box.get()\n",
    "    r = float(radius_box.get())\n",
    "    t = float(thickness_box.get()) * nm_to_um\n",
    "    w = float(width_box.get()) * nm_to_um\n",
    "    shape = shape_box.get()\n",
    "\n",
    "    # define simulation wavelength range\n",
    "    lda0, ldas = get_ldas()\n",
    "    freq0 = td.C_0 / lda0\n",
    "    freqs = td.C_0 / ldas\n",
    "    fwidth = 0.5 * (np.max(freqs) - np.min(freqs))\n",
    "\n",
    "    # create structures\n",
    "    if shape == \"Circular\":\n",
    "        x, y = circular_xy(r)\n",
    "    elif shape == \"Euler\":\n",
    "        x, y = euler_xy(r)\n",
    "\n",
    "    bend = line_to_structure(x, y, r, t, w, wg_mat)\n",
    "\n",
    "    sub = td.Structure(\n",
    "        geometry=td.Box.from_bounds(rmin=(-2 * r, -2 * r, -2 * r), rmax=(2 * r, 2 * r, 0)),\n",
    "        medium=media[sub_mat],\n",
    "    )\n",
    "\n",
    "    # define source and monitor\n",
    "    target_neff = media[wg_mat].nk_model(freq0)[0]\n",
    "    mode_spec = td.ModeSpec(num_modes=1, target_neff=target_neff)\n",
    "    mode_source = td.ModeSource(\n",
    "        center=(-lda0 / 2, 0, t / 2),\n",
    "        size=(0, 6 * w, 8 * t),\n",
    "        source_time=td.GaussianPulse(freq0=freq0, fwidth=fwidth),\n",
    "        direction=\"+\",\n",
    "        mode_spec=mode_spec,\n",
    "        mode_index=0,\n",
    "    )\n",
    "\n",
    "    # add a mode monitor to measure transmission\n",
    "    mode_monitor = td.ModeMonitor(\n",
    "        center=(r, r + lda0 / 2, t / 2),\n",
    "        size=(6 * w, 0, 8 * t),\n",
    "        freqs=freqs,\n",
    "        mode_spec=mode_spec,\n",
    "        name=\"mode\",\n",
    "    )\n",
    "\n",
    "    run_time = 15 * np.pi * r / td.C_0  # simulation run time\n",
    "\n",
    "    # define simulation\n",
    "    sim = td.Simulation(\n",
    "        center=(r / 2, r / 2, t / 2),\n",
    "        size=(r + 2 * w + lda0, r + 2 * w + lda0, 2 * lda0),\n",
    "        grid_spec=td.GridSpec.auto(min_steps_per_wvl=min_steps_per_wvl, wavelength=lda0),\n",
    "        structures=[bend, sub],\n",
    "        sources=[mode_source],\n",
    "        monitors=[mode_monitor],\n",
    "        run_time=run_time,\n",
    "        boundary_spec=td.BoundarySpec.all_sides(boundary=td.PML()),\n",
    "        medium=media[clad_mat],\n",
    "    )\n",
    "    return sim"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7be45962",
   "metadata": {},
   "source": [
    "Lastly, we need to define a few more functions to visualize the simulation, run the simulation, and plot the simulation result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "2897cf9b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# function to plot the simulation cross sections as well as the grid\n",
    "def plot_sim():\n",
    "    t = float(thickness_box.get()) * nm_to_um\n",
    "    w = float(width_box.get()) * nm_to_um\n",
    "\n",
    "    sim = make_sim()\n",
    "    ax1.clear()\n",
    "    sim.plot(z=t / 2, ax=ax1)\n",
    "    xy_canvas.draw()\n",
    "\n",
    "    ax3.clear()\n",
    "    sim.plot(x=0, ax=ax3)\n",
    "    sim.plot_grid(x=0, ax=ax3)\n",
    "    ax3.set_xlim(-w, w)\n",
    "    ax3.set_ylim(-2 * t, 2 * t)\n",
    "    yz_canvas.draw()\n",
    "\n",
    "\n",
    "# function to run the simulation\n",
    "def run_sim():\n",
    "    ax2.clear()\n",
    "    result_canvas.draw()\n",
    "    sim = make_sim()\n",
    "    job = web.Job(simulation=sim, task_name=\"Bend\")\n",
    "    text_widget.insert(\n",
    "        \"end\",\n",
    "        \"View your job in the web GUI at \"\n",
    "        + f\"tidy3d.simulation.cloud/workbench?taskId={job.task_id}.\\n\",\n",
    "    )\n",
    "    text_widget.insert(\"end\", \"Simulation started. Please wait.\\n\")\n",
    "    text_widget.insert(\"end\", \"Once completed, bending loss will be automatically plotted.\\n\")\n",
    "    text_widget.see(tk.END)\n",
    "    root.update_idletasks()\n",
    "    sim_data = job.run()\n",
    "    text_widget.insert(\"end\", \"Simulation completed.\\n\")\n",
    "    text_widget.see(tk.END)\n",
    "\n",
    "    return sim_data\n",
    "\n",
    "\n",
    "# function to plot the simulation result\n",
    "def plot_result():\n",
    "    _, ldas = get_ldas()\n",
    "    sim_data = run_sim()\n",
    "    amp = sim_data[\"mode\"].amps.sel(mode_index=0, direction=\"+\")\n",
    "    T = np.abs(amp) ** 2\n",
    "\n",
    "    ax2.plot(ldas / nm_to_um, 10 * np.log10(T), c=\"red\", linewidth=2)\n",
    "    ax2.set_title(\"Bending loss (dB)\")\n",
    "    ax2.set_xlabel(\"Wavelength (nm)\")\n",
    "    result_canvas.draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2ded523e",
   "metadata": {},
   "source": [
    "With all the preparation works ready, we can go ahead and start our GUI layout design. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "d8b7811e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set up the main window\n",
    "root = tk.Tk()\n",
    "root.title(\"90 degree bend simulator\")\n",
    "root.configure(bg=\"white\")\n",
    "root.resizable(False, False)\n",
    "\n",
    "# add a combo box for substrate material selection\n",
    "substrate_label = tk.Label(root, text=\"Substrate material:\", bg=\"white\")\n",
    "substrate_label.grid(column=0, row=0, sticky=\"E\")\n",
    "substrate_box = ttk.Combobox(root, values=materials, width=8)\n",
    "substrate_box.grid(column=1, row=0, sticky=\"W\")\n",
    "substrate_box.current(1)\n",
    "\n",
    "# add a combo box for cladding material selection\n",
    "cladding_label = tk.Label(root, text=\"Cladding material:\", bg=\"white\")\n",
    "cladding_label.grid(column=0, row=1, sticky=\"E\")\n",
    "cladding_box = ttk.Combobox(root, values=materials, width=8)\n",
    "cladding_box.grid(column=1, row=1, sticky=\"W\")\n",
    "cladding_box.current(3)\n",
    "\n",
    "# add a combo box for waveguide material selection\n",
    "wg_label = tk.Label(root, text=\"Waveguide material:\", bg=\"white\")\n",
    "wg_label.grid(column=0, row=2, sticky=\"E\")\n",
    "wg_box = ttk.Combobox(root, values=materials, width=8)\n",
    "wg_box.grid(column=1, row=2, sticky=\"W\")\n",
    "wg_box.current(0)\n",
    "\n",
    "# add a combo box for bend shape selection\n",
    "shape_label = tk.Label(root, text=\"Bend shape:\", bg=\"white\")\n",
    "shape_label.grid(column=0, row=3, sticky=\"E\")\n",
    "shape_box = ttk.Combobox(root, values=shapes, width=8)\n",
    "shape_box.grid(column=1, row=3, sticky=\"W\")\n",
    "shape_box.current(0)\n",
    "\n",
    "# add a label and a input box for waveguide width\n",
    "width_label = tk.Label(root, text=\"Waveguide width (nm):\", bg=\"white\")\n",
    "width_label.grid(column=2, row=0, sticky=\"E\")\n",
    "width_box = tk.Entry(root, width=6)\n",
    "width_box.grid(column=3, row=0, sticky=\"W\")\n",
    "width_box.insert(0, \"500\")\n",
    "\n",
    "# add a label and a input box for waveguide thickness\n",
    "thickness_label = tk.Label(root, text=\"Waveguide thickness (nm):\", bg=\"white\")\n",
    "thickness_label.grid(column=2, row=1, sticky=\"E\")\n",
    "thickness_box = tk.Entry(root, width=6)\n",
    "thickness_box.grid(column=3, row=1, sticky=\"W\")\n",
    "thickness_box.insert(0, \"220\")\n",
    "\n",
    "# add a label and a input box for waveguide radius\n",
    "radius_label = tk.Label(root, text=\"Bend radius (um):\", bg=\"white\")\n",
    "radius_label.grid(column=2, row=2, sticky=\"E\")\n",
    "radius_box = tk.Entry(root, width=6)\n",
    "radius_box.grid(column=3, row=2, sticky=\"W\")\n",
    "radius_box.insert(0, \"5\")\n",
    "\n",
    "# add a label and a input box for central wavelength\n",
    "lda0_label = tk.Label(root, text=\"Central wavelength (nm):\", bg=\"white\")\n",
    "lda0_label.grid(column=2, row=3, sticky=\"E\")\n",
    "lda0_box = tk.Entry(root, width=6)\n",
    "lda0_box.grid(column=3, row=3, sticky=\"W\")\n",
    "lda0_box.insert(0, \"1550\")\n",
    "\n",
    "# add a label and a input box for bandwidth\n",
    "bw_label = tk.Label(root, text=\"Bandwidth (nm):\", bg=\"white\")\n",
    "bw_label.grid(column=2, row=4, sticky=\"E\")\n",
    "bw_box = tk.Entry(root, width=6)\n",
    "bw_box.grid(column=3, row=4, sticky=\"W\")\n",
    "bw_box.insert(0, \"50\")\n",
    "\n",
    "# add a button for viewing simulation\n",
    "view_button = tk.Button(root, text=\"View simulation\", command=plot_sim)\n",
    "view_button.grid(column=0, row=5, sticky=\"E\")\n",
    "\n",
    "# add a button for running simulation\n",
    "run_button = tk.Button(root, text=\"Run simulation\", command=plot_result)\n",
    "run_button.grid(column=2, row=5, sticky=\"E\")\n",
    "\n",
    "# add a plot for xy cross section\n",
    "fig1, ax1 = plt.subplots(figsize=(3, 3), tight_layout=True)\n",
    "xy_canvas = FigureCanvasTkAgg(fig1, master=root)\n",
    "xy_canvas_widget = xy_canvas.get_tk_widget()\n",
    "xy_canvas_widget.grid(column=0, row=6, columnspan=2)\n",
    "plt.close(fig1)\n",
    "\n",
    "# add a plot for bending loss spectrum\n",
    "fig2, ax2 = plt.subplots(figsize=(5, 3), tight_layout=True)\n",
    "result_canvas = FigureCanvasTkAgg(fig2, master=root)\n",
    "result_canvas_widget = result_canvas.get_tk_widget()\n",
    "result_canvas_widget.grid(column=2, row=6, columnspan=2)\n",
    "plt.close(fig2)\n",
    "\n",
    "# add a plot for yz cross section\n",
    "fig3, ax3 = plt.subplots(figsize=(3, 3), tight_layout=True)\n",
    "yz_canvas = FigureCanvasTkAgg(fig3, master=root)\n",
    "yz_canvas_widget = yz_canvas.get_tk_widget()\n",
    "yz_canvas_widget.grid(column=0, row=7, columnspan=2)\n",
    "plt.close(fig3)\n",
    "\n",
    "# add a text box to display information\n",
    "text_widget = tk.Text(root, height=12, width=1)\n",
    "text_widget.grid(column=2, row=7, columnspan=2, sticky=\"ew\")\n",
    "default_text = \"Click `View simulation` to view the simulation setup.\\n\"\n",
    "text_widget.insert(\"1.0\", default_text)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9fc9730",
   "metadata": {},
   "source": [
    "With everything ready, we can now launch the GUI."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "12be563a",
   "metadata": {},
   "outputs": [],
   "source": [
    "root.mainloop()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f6212dd",
   "metadata": {},
   "source": [
    "In conclusion, this notebook demonstrates how to make a simple purpose-specific GUI for Tidy3D. By following this workflow, users have the full freedom to create GUIs that suit their particular workflow and experience level. More complex features and visualizations can be implemented to further enhance the usability of the GUI if desired. Simple GUIs are a great way to get inexperienced users around you to get started with Tidy3D.\n",
    "\n",
    "For easier distribution, one can package this GUI into a stand-alone executable following tutorials such as [here](https://www.pythonguis.com/tutorials/packaging-tkinter-applications-windows-pyinstaller/). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "afa4cd4c",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "description": "This notebook demonstrates how to use tkinter to develop a purpose-specific GUI for Tidy3D.",
  "feature_image": "./img/tkinter_gui.png",
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "keywords": "GUI, tkinter, waveguide bend, Tidy3D, FDTD",
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.0"
  },
  "title": "Waveguide Bend Simulator | Flexcompute"
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
